Merge remote-tracking branch 'remotes/armbru/tags/pull-qobject-2018-08-24' into staging

QObject patches for 2018-08-24

# gpg: Signature made Fri 24 Aug 2018 20:28:53 BST
# gpg:                using RSA key 3870B400EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>"
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-qobject-2018-08-24: (58 commits)
  json: Update references to RFC 7159 to RFC 8259
  json: Support %% in JSON strings when interpolating
  json: Improve safety of qobject_from_jsonf_nofail() & friends
  json: Keep interpolation state in JSONParserContext
  tests/drive_del-test: Fix harmless JSON interpolation bug
  json: Clean up headers
  qobject: Drop superfluous includes of qemu-common.h
  json: Make JSONToken opaque outside json-parser.c
  json: Unbox tokens queue in JSONMessageParser
  json: Streamline json_message_process_token()
  json: Enforce token count and size limits more tightly
  qjson: Have qobject_from_json() & friends reject empty and blank
  json: Assert json_parser_parse() consumes all tokens on success
  json: Fix streamer not to ignore trailing unterminated structures
  json: Fix latent parser aborts at end of input
  qjson: Fix qobject_from_json() & friends for multiple values
  json: Improve names of lexer states related to numbers
  json: Replace %I64d, %I64u by %PRId64, %PRIu64
  json: Leave rejecting invalid interpolation to parser
  json: Pass lexical errors and limit violations to callback
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2018-08-25 10:11:54 +01:00
34 changed files with 1478 additions and 1335 deletions
+1
View File
@@ -1715,6 +1715,7 @@ F: monitor.c
F: docs/devel/*qmp-*
F: scripts/qmp/
F: tests/qmp-test.c
F: tests/qmp-cmd-test.c
T: git git://repo.or.cz/qemu/armbru.git qapi-next
qtest
-5
View File
@@ -1478,11 +1478,6 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
options_obj = qobject_from_json(filename, errp);
if (!options_obj) {
/* Work around qobject_from_json() lossage TODO fix that */
if (errp && !*errp) {
error_setg(errp, "Could not parse the JSON options");
return NULL;
}
error_prepend(errp, "Could not parse the JSON options: ");
return NULL;
}
+28 -14
View File
@@ -20,9 +20,9 @@ operating system.
2. Protocol Specification
=========================
This section details the protocol format. For the purpose of this document
"Client" is any application which is using QMP to communicate with QEMU and
"Server" is QEMU itself.
This section details the protocol format. For the purpose of this
document, "Server" is either QEMU or the QEMU Guest Agent, and
"Client" is any application communicating with it via QMP.
JSON data structures, when mentioned in this document, are always in the
following format:
@@ -34,9 +34,8 @@ by the JSON standard:
http://www.ietf.org/rfc/rfc7159.txt
The protocol is always encoded in UTF-8 except for synchronization
bytes (documented below); although thanks to json-string escape
sequences, the server will reply using only the strict ASCII subset.
The server expects its input to be encoded in UTF-8, and sends its
output encoded in ASCII.
For convenience, json-object members mentioned in this document will
be in a certain order. However, in real protocol usage they can be in
@@ -215,16 +214,31 @@ Some events are rate-limited to at most one per second. If additional
dropped, and the last one is delayed. "Similar" normally means same
event type. See qmp-events.txt for details.
2.6 QGA Synchronization
2.6 Forcing the JSON parser into known-good state
-------------------------------------------------
Incomplete or invalid input can leave the server's JSON parser in a
state where it can't parse additional commands. To get it back into
known-good state, the client should provoke a lexical error.
The cleanest way to do that is sending an ASCII control character
other than '\t' (horizontal tab), '\r' (carriage return), or '\n' (new
line).
Sadly, older versions of QEMU can fail to flag this as an error. If a
client needs to deal with them, it should send a 0xFF byte.
2.7 QGA Synchronization
-----------------------
When using QGA, an additional synchronization feature is built into
the protocol. If the Client sends a raw 0xFF sentinel byte (not valid
JSON), then the Server will reset its state and discard all pending
data prior to the sentinel. Conversely, if the Client makes use of
the 'guest-sync-delimited' command, the Server will send a raw 0xFF
sentinel byte prior to its response, to aid the Client in discarding
any data prior to the sentinel.
When a client connects to QGA over a transport lacking proper
connection semantics such as virtio-serial, QGA may have read partial
input from a previous client. The client needs to force QGA's parser
into known-good state using the previous section's technique.
Moreover, the client may receive output a previous client didn't read.
To help with skipping that output, QGA provides the
'guest-sync-delimited' command. Refer to its documentation for
details.
3. QMP Examples
-56
View File
@@ -1,56 +0,0 @@
/*
* JSON lexer
*
* Copyright IBM, Corp. 2009
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
* 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.
*
*/
#ifndef QEMU_JSON_LEXER_H
#define QEMU_JSON_LEXER_H
typedef enum json_token_type {
JSON_MIN = 100,
JSON_LCURLY = JSON_MIN,
JSON_RCURLY,
JSON_LSQUARE,
JSON_RSQUARE,
JSON_COLON,
JSON_COMMA,
JSON_INTEGER,
JSON_FLOAT,
JSON_KEYWORD,
JSON_STRING,
JSON_ESCAPE,
JSON_SKIP,
JSON_ERROR,
} JSONTokenType;
typedef struct JSONLexer JSONLexer;
typedef void (JSONLexerEmitter)(JSONLexer *, GString *,
JSONTokenType, int x, int y);
struct JSONLexer
{
JSONLexerEmitter *emit;
int state;
GString *token;
int x, y;
};
void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func);
int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size);
int json_lexer_flush(JSONLexer *lexer);
void json_lexer_destroy(JSONLexer *lexer);
#endif
+30 -6
View File
@@ -1,5 +1,5 @@
/*
* JSON Parser
* JSON Parser
*
* Copyright IBM, Corp. 2009
*
@@ -11,12 +11,36 @@
*
*/
#ifndef QEMU_JSON_PARSER_H
#define QEMU_JSON_PARSER_H
#ifndef QAPI_QMP_JSON_PARSER_H
#define QAPI_QMP_JSON_PARSER_H
#include "qemu-common.h"
typedef struct JSONLexer {
int start_state, state;
GString *token;
int x, y;
} JSONLexer;
QObject *json_parser_parse(GQueue *tokens, va_list *ap);
QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp);
typedef struct JSONMessageParser {
void (*emit)(void *opaque, QObject *json, Error *err);
void *opaque;
va_list *ap;
JSONLexer lexer;
int brace_count;
int bracket_count;
GQueue tokens;
uint64_t token_size;
} JSONMessageParser;
void json_message_parser_init(JSONMessageParser *parser,
void (*emit)(void *opaque, QObject *json,
Error *err),
void *opaque, va_list *ap);
void json_message_parser_feed(JSONMessageParser *parser,
const char *buffer, size_t size);
void json_message_parser_flush(JSONMessageParser *parser);
void json_message_parser_destroy(JSONMessageParser *parser);
#endif
-46
View File
@@ -1,46 +0,0 @@
/*
* JSON streaming support
*
* Copyright IBM, Corp. 2009
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
* 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.
*
*/
#ifndef QEMU_JSON_STREAMER_H
#define QEMU_JSON_STREAMER_H
#include "qapi/qmp/json-lexer.h"
typedef struct JSONToken {
int type;
int x;
int y;
char str[];
} JSONToken;
typedef struct JSONMessageParser
{
void (*emit)(struct JSONMessageParser *parser, GQueue *tokens);
JSONLexer lexer;
int brace_count;
int bracket_count;
GQueue *tokens;
uint64_t token_size;
} JSONMessageParser;
void json_message_parser_init(JSONMessageParser *parser,
void (*func)(JSONMessageParser *, GQueue *));
int json_message_parser_feed(JSONMessageParser *parser,
const char *buffer, size_t size);
int json_message_parser_flush(JSONMessageParser *parser);
void json_message_parser_destroy(JSONMessageParser *parser);
#endif
-3
View File
@@ -61,9 +61,6 @@
#define QERR_IO_ERROR \
"An IO error has occurred"
#define QERR_JSON_PARSING \
"Invalid JSON syntax"
#define QERR_MIGRATION_ACTIVE \
"There's a migration process in progress"
+1 -1
View File
@@ -25,7 +25,7 @@ typedef enum {
/*
* QNum encapsulates how our dialect of JSON fills in the blanks left
* by the JSON specification (RFC 7159) regarding numbers.
* by the JSON specification (RFC 8259) regarding numbers.
*
* Conceptually, we treat number as an abstract type with three
* concrete subtypes: floating-point, signed integer, unsigned
+1
View File
@@ -2,5 +2,6 @@
#define QEMU_UNICODE_H
int mod_utf8_codepoint(const char *s, size_t n, char **end);
ssize_t mod_utf8_encode(char buf[], size_t bufsz, int codepoint);
#endif
+8 -13
View File
@@ -58,7 +58,6 @@
#include "qapi/qmp/qnum.h"
#include "qapi/qmp/qstring.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/json-parser.h"
#include "qapi/qmp/qlist.h"
#include "qom/object_interfaces.h"
@@ -4256,20 +4255,14 @@ static void monitor_qmp_bh_dispatcher(void *data)
#define QMP_REQ_QUEUE_LEN_MAX (8)
static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
static void handle_qmp_command(void *opaque, QObject *req, Error *err)
{
QObject *req, *id = NULL;
Monitor *mon = opaque;
QObject *id = NULL;
QDict *qdict;
MonitorQMP *mon_qmp = container_of(parser, MonitorQMP, parser);
Monitor *mon = container_of(mon_qmp, Monitor, qmp);
Error *err = NULL;
QMPRequest *req_obj;
req = json_parser_parse_err(tokens, NULL, &err);
if (!req && !err) {
/* json_parser_parse_err() sucks: can fail without setting @err */
error_setg(&err, QERR_JSON_PARSING);
}
assert(!req != !err);
qdict = qobject_to(QDict, req);
if (qdict) {
@@ -4465,7 +4458,8 @@ static void monitor_qmp_event(void *opaque, int event)
monitor_qmp_response_flush(mon);
monitor_qmp_cleanup_queues(mon);
json_message_parser_destroy(&mon->qmp.parser);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command,
mon, NULL);
mon_refcount--;
monitor_fdsets_cleanup();
break;
@@ -4683,7 +4677,8 @@ void monitor_init(Chardev *chr, int flags)
if (monitor_is_qmp(mon)) {
qemu_chr_fe_set_echo(&mon->chr, true);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command,
mon, NULL);
if (mon->use_io_thread) {
/*
* Make sure the old iowatch is gone. It's possible when
+1 -1
View File
@@ -120,7 +120,7 @@
##
# @JSONType:
#
# The four primitive and two structured types according to RFC 7159
# The four primitive and two structured types according to RFC 8259
# section 1, plus 'int' (split off 'number'), plus the obvious top
# type 'value'.
#
-1
View File
@@ -14,7 +14,6 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/qmp/dispatch.h"
#include "qapi/qmp/json-parser.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qbool.h"
-5
View File
@@ -725,11 +725,6 @@ Visitor *qobject_input_visitor_new_str(const char *str,
if (is_json) {
obj = qobject_from_json(str, errp);
if (!obj) {
/* Work around qobject_from_json() lossage TODO fix that */
if (errp && !*errp) {
error_setg(errp, "JSON parse error");
return NULL;
}
return NULL;
}
args = qobject_to(QDict, obj);
+5 -10
View File
@@ -18,7 +18,6 @@
#include <syslog.h>
#include <sys/wait.h>
#endif
#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/json-parser.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qjson.h"
@@ -597,24 +596,20 @@ static void process_command(GAState *s, QDict *req)
}
/* handle requests/control events coming in over the channel */
static void process_event(JSONMessageParser *parser, GQueue *tokens)
static void process_event(void *opaque, QObject *obj, Error *err)
{
GAState *s = container_of(parser, GAState, parser);
QObject *obj;
GAState *s = opaque;
QDict *req, *rsp;
Error *err = NULL;
int ret;
g_assert(s && parser);
g_debug("process_event: called");
obj = json_parser_parse_err(tokens, NULL, &err);
assert(!obj != !err);
if (err) {
goto err;
}
req = qobject_to(QDict, obj);
if (!req) {
error_setg(&err, QERR_JSON_PARSING);
error_setg(&err, "Input must be a JSON object");
goto err;
}
if (!qdict_haskey(req, "execute")) {
@@ -1320,7 +1315,7 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation)
s->command_state = ga_command_state_new();
ga_command_state_init(s, s->command_state);
ga_command_state_init_all(s->command_state);
json_message_parser_init(&s->parser, process_event);
json_message_parser_init(&s->parser, process_event, s, NULL);
#ifndef _WIN32
if (!register_signal_handlers()) {
+139 -174
View File
@@ -12,63 +12,116 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qapi/qmp/json-lexer.h"
#include "json-parser-int.h"
#define MAX_TOKEN_SIZE (64ULL << 20)
/*
* Required by JSON (RFC 7159):
* From RFC 8259 "The JavaScript Object Notation (JSON) Data
* Interchange Format", with [comments in brackets]:
*
* \"([^\\\"]|\\[\"'\\/bfnrt]|\\u[0-9a-fA-F]{4})*\"
* -?(0|[1-9][0-9]*)(.[0-9]+)?([eE][-+]?[0-9]+)?
* [{}\[\],:]
* [a-z]+ # covers null, true, false
* The set of tokens includes six structural characters, strings,
* numbers, and three literal names.
*
* Extension of '' strings:
* These are the six structural characters:
*
* '([^\\']|\\[\"'\\/bfnrt]|\\u[0-9a-fA-F]{4})*'
* begin-array = ws %x5B ws ; [ left square bracket
* begin-object = ws %x7B ws ; { left curly bracket
* end-array = ws %x5D ws ; ] right square bracket
* end-object = ws %x7D ws ; } right curly bracket
* name-separator = ws %x3A ws ; : colon
* value-separator = ws %x2C ws ; , comma
*
* Extension for vararg handling in JSON construction:
* Insignificant whitespace is allowed before or after any of the six
* structural characters.
* [This lexer accepts it before or after any token, which is actually
* the same, as the grammar always has structural characters between
* other tokens.]
*
* %((l|ll|I64)?d|[ipsf])
* ws = *(
* %x20 / ; Space
* %x09 / ; Horizontal tab
* %x0A / ; Line feed or New line
* %x0D ) ; Carriage return
*
* [...] three literal names:
* false null true
* [This lexer accepts [a-z]+, and leaves rejecting unknown literal
* names to the parser.]
*
* [Numbers:]
*
* number = [ minus ] int [ frac ] [ exp ]
* decimal-point = %x2E ; .
* digit1-9 = %x31-39 ; 1-9
* e = %x65 / %x45 ; e E
* exp = e [ minus / plus ] 1*DIGIT
* frac = decimal-point 1*DIGIT
* int = zero / ( digit1-9 *DIGIT )
* minus = %x2D ; -
* plus = %x2B ; +
* zero = %x30 ; 0
*
* [Strings:]
* string = quotation-mark *char quotation-mark
*
* char = unescaped /
* escape (
* %x22 / ; " quotation mark U+0022
* %x5C / ; \ reverse solidus U+005C
* %x2F / ; / solidus U+002F
* %x62 / ; b backspace U+0008
* %x66 / ; f form feed U+000C
* %x6E / ; n line feed U+000A
* %x72 / ; r carriage return U+000D
* %x74 / ; t tab U+0009
* %x75 4HEXDIG ) ; uXXXX U+XXXX
* escape = %x5C ; \
* quotation-mark = %x22 ; "
* unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
* [This lexer accepts any non-control character after escape, and
* leaves rejecting invalid ones to the parser.]
*
*
* Extensions over RFC 8259:
* - Extra escape sequence in strings:
* 0x27 (apostrophe) is recognized after escape, too
* - Single-quoted strings:
* Like double-quoted strings, except they're delimited by %x27
* (apostrophe) instead of %x22 (quotation mark), and can't contain
* unescaped apostrophe, but can contain unescaped quotation mark.
* - Interpolation, if enabled:
* The lexer accepts %[A-Za-z0-9]*, and leaves rejecting invalid
* ones to the parser.
*
* Note:
* - Input must be encoded in modified UTF-8.
* - Decoding and validating is left to the parser.
*/
enum json_lexer_state {
IN_ERROR = 0, /* must really be 0, see json_lexer[] */
IN_DQ_UCODE3,
IN_DQ_UCODE2,
IN_DQ_UCODE1,
IN_DQ_UCODE0,
IN_DQ_STRING_ESCAPE,
IN_DQ_STRING,
IN_SQ_UCODE3,
IN_SQ_UCODE2,
IN_SQ_UCODE1,
IN_SQ_UCODE0,
IN_SQ_STRING_ESCAPE,
IN_SQ_STRING,
IN_ZERO,
IN_DIGITS,
IN_DIGIT,
IN_EXP_DIGITS,
IN_EXP_SIGN,
IN_EXP_E,
IN_MANTISSA,
IN_MANTISSA_DIGITS,
IN_NONZERO_NUMBER,
IN_NEG_NONZERO_NUMBER,
IN_DIGITS,
IN_SIGN,
IN_KEYWORD,
IN_ESCAPE,
IN_ESCAPE_L,
IN_ESCAPE_LL,
IN_ESCAPE_I,
IN_ESCAPE_I6,
IN_ESCAPE_I64,
IN_INTERP,
IN_WHITESPACE,
IN_START,
IN_START_INTERP, /* must be IN_START + 1 */
};
QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START);
QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START_INTERP);
QEMU_BUILD_BUG_ON(IN_START_INTERP != IN_START + 1);
#define TERMINAL(state) [0 ... 0x7F] = (state)
@@ -76,87 +129,27 @@ QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START);
from OLD_STATE required lookahead. This happens whenever the table
below uses the TERMINAL macro. */
#define TERMINAL_NEEDED_LOOKAHEAD(old_state, terminal) \
(json_lexer[(old_state)][0] == (terminal))
(terminal != IN_ERROR && json_lexer[(old_state)][0] == (terminal))
static const uint8_t json_lexer[][256] = {
/* Relies on default initialization to IN_ERROR! */
/* double quote string */
[IN_DQ_UCODE3] = {
['0' ... '9'] = IN_DQ_STRING,
['a' ... 'f'] = IN_DQ_STRING,
['A' ... 'F'] = IN_DQ_STRING,
},
[IN_DQ_UCODE2] = {
['0' ... '9'] = IN_DQ_UCODE3,
['a' ... 'f'] = IN_DQ_UCODE3,
['A' ... 'F'] = IN_DQ_UCODE3,
},
[IN_DQ_UCODE1] = {
['0' ... '9'] = IN_DQ_UCODE2,
['a' ... 'f'] = IN_DQ_UCODE2,
['A' ... 'F'] = IN_DQ_UCODE2,
},
[IN_DQ_UCODE0] = {
['0' ... '9'] = IN_DQ_UCODE1,
['a' ... 'f'] = IN_DQ_UCODE1,
['A' ... 'F'] = IN_DQ_UCODE1,
},
[IN_DQ_STRING_ESCAPE] = {
['b'] = IN_DQ_STRING,
['f'] = IN_DQ_STRING,
['n'] = IN_DQ_STRING,
['r'] = IN_DQ_STRING,
['t'] = IN_DQ_STRING,
['/'] = IN_DQ_STRING,
['\\'] = IN_DQ_STRING,
['\''] = IN_DQ_STRING,
['\"'] = IN_DQ_STRING,
['u'] = IN_DQ_UCODE0,
[0x20 ... 0xFD] = IN_DQ_STRING,
},
[IN_DQ_STRING] = {
[1 ... 0xBF] = IN_DQ_STRING,
[0xC2 ... 0xF4] = IN_DQ_STRING,
[0x20 ... 0xFD] = IN_DQ_STRING,
['\\'] = IN_DQ_STRING_ESCAPE,
['"'] = JSON_STRING,
},
/* single quote string */
[IN_SQ_UCODE3] = {
['0' ... '9'] = IN_SQ_STRING,
['a' ... 'f'] = IN_SQ_STRING,
['A' ... 'F'] = IN_SQ_STRING,
},
[IN_SQ_UCODE2] = {
['0' ... '9'] = IN_SQ_UCODE3,
['a' ... 'f'] = IN_SQ_UCODE3,
['A' ... 'F'] = IN_SQ_UCODE3,
},
[IN_SQ_UCODE1] = {
['0' ... '9'] = IN_SQ_UCODE2,
['a' ... 'f'] = IN_SQ_UCODE2,
['A' ... 'F'] = IN_SQ_UCODE2,
},
[IN_SQ_UCODE0] = {
['0' ... '9'] = IN_SQ_UCODE1,
['a' ... 'f'] = IN_SQ_UCODE1,
['A' ... 'F'] = IN_SQ_UCODE1,
},
[IN_SQ_STRING_ESCAPE] = {
['b'] = IN_SQ_STRING,
['f'] = IN_SQ_STRING,
['n'] = IN_SQ_STRING,
['r'] = IN_SQ_STRING,
['t'] = IN_SQ_STRING,
['/'] = IN_SQ_STRING,
['\\'] = IN_SQ_STRING,
['\''] = IN_SQ_STRING,
['\"'] = IN_SQ_STRING,
['u'] = IN_SQ_UCODE0,
[0x20 ... 0xFD] = IN_SQ_STRING,
},
[IN_SQ_STRING] = {
[1 ... 0xBF] = IN_SQ_STRING,
[0xC2 ... 0xF4] = IN_SQ_STRING,
[0x20 ... 0xFD] = IN_SQ_STRING,
['\\'] = IN_SQ_STRING_ESCAPE,
['\''] = JSON_STRING,
},
@@ -169,19 +162,19 @@ static const uint8_t json_lexer[][256] = {
},
/* Float */
[IN_DIGITS] = {
[IN_EXP_DIGITS] = {
TERMINAL(JSON_FLOAT),
['0' ... '9'] = IN_DIGITS,
['0' ... '9'] = IN_EXP_DIGITS,
},
[IN_DIGIT] = {
['0' ... '9'] = IN_DIGITS,
[IN_EXP_SIGN] = {
['0' ... '9'] = IN_EXP_DIGITS,
},
[IN_EXP_E] = {
['-'] = IN_DIGIT,
['+'] = IN_DIGIT,
['0' ... '9'] = IN_DIGITS,
['-'] = IN_EXP_SIGN,
['+'] = IN_EXP_SIGN,
['0' ... '9'] = IN_EXP_DIGITS,
},
[IN_MANTISSA_DIGITS] = {
@@ -196,17 +189,17 @@ static const uint8_t json_lexer[][256] = {
},
/* Number */
[IN_NONZERO_NUMBER] = {
[IN_DIGITS] = {
TERMINAL(JSON_INTEGER),
['0' ... '9'] = IN_NONZERO_NUMBER,
['0' ... '9'] = IN_DIGITS,
['e'] = IN_EXP_E,
['E'] = IN_EXP_E,
['.'] = IN_MANTISSA,
},
[IN_NEG_NONZERO_NUMBER] = {
[IN_SIGN] = {
['0'] = IN_ZERO,
['1' ... '9'] = IN_NONZERO_NUMBER,
['1' ... '9'] = IN_DIGITS,
},
/* keywords */
@@ -224,49 +217,25 @@ static const uint8_t json_lexer[][256] = {
['\n'] = IN_WHITESPACE,
},
/* escape */
[IN_ESCAPE_LL] = {
['d'] = JSON_ESCAPE,
['u'] = JSON_ESCAPE,
/* interpolation */
[IN_INTERP] = {
TERMINAL(JSON_INTERP),
['A' ... 'Z'] = IN_INTERP,
['a' ... 'z'] = IN_INTERP,
['0' ... '9'] = IN_INTERP,
},
[IN_ESCAPE_L] = {
['d'] = JSON_ESCAPE,
['l'] = IN_ESCAPE_LL,
['u'] = JSON_ESCAPE,
},
[IN_ESCAPE_I64] = {
['d'] = JSON_ESCAPE,
['u'] = JSON_ESCAPE,
},
[IN_ESCAPE_I6] = {
['4'] = IN_ESCAPE_I64,
},
[IN_ESCAPE_I] = {
['6'] = IN_ESCAPE_I6,
},
[IN_ESCAPE] = {
['d'] = JSON_ESCAPE,
['i'] = JSON_ESCAPE,
['p'] = JSON_ESCAPE,
['s'] = JSON_ESCAPE,
['u'] = JSON_ESCAPE,
['f'] = JSON_ESCAPE,
['l'] = IN_ESCAPE_L,
['I'] = IN_ESCAPE_I,
},
/* top level rule */
[IN_START] = {
/*
* Two start states:
* - IN_START recognizes JSON tokens with our string extensions
* - IN_START_INTERP additionally recognizes interpolation.
*/
[IN_START ... IN_START_INTERP] = {
['"'] = IN_DQ_STRING,
['\''] = IN_SQ_STRING,
['0'] = IN_ZERO,
['1' ... '9'] = IN_NONZERO_NUMBER,
['-'] = IN_NEG_NONZERO_NUMBER,
['1' ... '9'] = IN_DIGITS,
['-'] = IN_SIGN,
['{'] = JSON_LCURLY,
['}'] = JSON_RCURLY,
['['] = JSON_LSQUARE,
@@ -274,23 +243,23 @@ static const uint8_t json_lexer[][256] = {
[','] = JSON_COMMA,
[':'] = JSON_COLON,
['a' ... 'z'] = IN_KEYWORD,
['%'] = IN_ESCAPE,
[' '] = IN_WHITESPACE,
['\t'] = IN_WHITESPACE,
['\r'] = IN_WHITESPACE,
['\n'] = IN_WHITESPACE,
},
[IN_START_INTERP]['%'] = IN_INTERP,
};
void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func)
void json_lexer_init(JSONLexer *lexer, bool enable_interpolation)
{
lexer->emit = func;
lexer->state = IN_START;
lexer->start_state = lexer->state = enable_interpolation
? IN_START_INTERP : IN_START;
lexer->token = g_string_sized_new(3);
lexer->x = lexer->y = 0;
}
static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
{
int char_consumed, new_state;
@@ -304,7 +273,7 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
assert(lexer->state <= ARRAY_SIZE(json_lexer));
new_state = json_lexer[lexer->state][(uint8_t)ch];
char_consumed = !TERMINAL_NEEDED_LOOKAHEAD(lexer->state, new_state);
if (char_consumed) {
if (char_consumed && !flush) {
g_string_append_c(lexer->token, ch);
}
@@ -315,23 +284,23 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
case JSON_RSQUARE:
case JSON_COLON:
case JSON_COMMA:
case JSON_ESCAPE:
case JSON_INTERP:
case JSON_INTEGER:
case JSON_FLOAT:
case JSON_KEYWORD:
case JSON_STRING:
lexer->emit(lexer, lexer->token, new_state, lexer->x, lexer->y);
json_message_process_token(lexer, lexer->token, new_state,
lexer->x, lexer->y);
/* fall through */
case JSON_SKIP:
g_string_truncate(lexer->token, 0);
new_state = IN_START;
new_state = lexer->start_state;
break;
case IN_ERROR:
/* XXX: To avoid having previous bad input leaving the parser in an
* unresponsive state where we consume unpredictable amounts of
* subsequent "good" input, percolate this error state up to the
* tokenizer/parser by forcing a NULL object to be emitted, then
* reset state.
* parser by emitting a JSON_ERROR token, then reset lexer state.
*
* Also note that this handling is required for reliable channel
* negotiation between QMP and the guest agent, since chr(0xFF)
@@ -340,11 +309,11 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
* never a valid ASCII/UTF-8 sequence, so this should reliably
* induce an error/flush state.
*/
lexer->emit(lexer, lexer->token, JSON_ERROR, lexer->x, lexer->y);
json_message_process_token(lexer, lexer->token, JSON_ERROR,
lexer->x, lexer->y);
g_string_truncate(lexer->token, 0);
new_state = IN_START;
lexer->state = new_state;
return 0;
lexer->state = lexer->start_state;
return;
default:
break;
}
@@ -355,33 +324,29 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
* this is a security consideration.
*/
if (lexer->token->len > MAX_TOKEN_SIZE) {
lexer->emit(lexer, lexer->token, lexer->state, lexer->x, lexer->y);
json_message_process_token(lexer, lexer->token, lexer->state,
lexer->x, lexer->y);
g_string_truncate(lexer->token, 0);
lexer->state = IN_START;
lexer->state = lexer->start_state;
}
return 0;
}
int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size)
void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size)
{
size_t i;
for (i = 0; i < size; i++) {
int err;
err = json_lexer_feed_char(lexer, buffer[i], false);
if (err < 0) {
return err;
}
json_lexer_feed_char(lexer, buffer[i], false);
}
return 0;
}
int json_lexer_flush(JSONLexer *lexer)
void json_lexer_flush(JSONLexer *lexer)
{
return lexer->state == IN_START ? 0 : json_lexer_feed_char(lexer, 0, true);
if (lexer->state != lexer->start_state) {
json_lexer_feed_char(lexer, 0, true);
}
json_message_process_token(lexer, lexer->token, JSON_END_OF_INPUT,
lexer->x, lexer->y);
}
void json_lexer_destroy(JSONLexer *lexer)
+54
View File
@@ -0,0 +1,54 @@
/*
* JSON Parser
*
* Copyright IBM, Corp. 2009
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
* 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.
*
*/
#ifndef JSON_PARSER_INT_H
#define JSON_PARSER_INT_H
#include "qapi/qmp/json-parser.h"
typedef enum json_token_type {
JSON_MIN = 100,
JSON_LCURLY = JSON_MIN,
JSON_RCURLY,
JSON_LSQUARE,
JSON_RSQUARE,
JSON_COLON,
JSON_COMMA,
JSON_INTEGER,
JSON_FLOAT,
JSON_KEYWORD,
JSON_STRING,
JSON_INTERP,
JSON_SKIP,
JSON_ERROR,
JSON_END_OF_INPUT,
} JSONTokenType;
typedef struct JSONToken JSONToken;
/* json-lexer.c */
void json_lexer_init(JSONLexer *lexer, bool enable_interpolation);
void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size);
void json_lexer_flush(JSONLexer *lexer);
void json_lexer_destroy(JSONLexer *lexer);
/* json-streamer.c */
void json_message_process_token(JSONLexer *lexer, GString *input,
JSONTokenType type, int x, int y);
/* json-parser.c */
JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr);
QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp);
#endif
+182 -197
View File
File diff suppressed because it is too large Load Diff
+60 -62
View File
@@ -12,34 +12,29 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qapi/qmp/json-lexer.h"
#include "qapi/qmp/json-streamer.h"
#include "qapi/error.h"
#include "json-parser-int.h"
#define MAX_TOKEN_SIZE (64ULL << 20)
#define MAX_TOKEN_COUNT (2ULL << 20)
#define MAX_NESTING (1ULL << 10)
static void json_message_free_token(void *token, void *opaque)
{
g_free(token);
}
#define MAX_NESTING (1 << 10)
static void json_message_free_tokens(JSONMessageParser *parser)
{
if (parser->tokens) {
g_queue_foreach(parser->tokens, json_message_free_token, NULL);
g_queue_free(parser->tokens);
parser->tokens = NULL;
JSONToken *token;
while ((token = g_queue_pop_head(&parser->tokens))) {
g_free(token);
}
}
static void json_message_process_token(JSONLexer *lexer, GString *input,
JSONTokenType type, int x, int y)
void json_message_process_token(JSONLexer *lexer, GString *input,
JSONTokenType type, int x, int y)
{
JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer);
QObject *json = NULL;
Error *err = NULL;
JSONToken *token;
GQueue *tokens;
switch (type) {
case JSON_LCURLY:
@@ -54,79 +49,82 @@ static void json_message_process_token(JSONLexer *lexer, GString *input,
case JSON_RSQUARE:
parser->bracket_count--;
break;
case JSON_ERROR:
error_setg(&err, "JSON parse error, stray '%s'", input->str);
goto out_emit;
case JSON_END_OF_INPUT:
if (g_queue_is_empty(&parser->tokens)) {
return;
}
json = json_parser_parse(&parser->tokens, parser->ap, &err);
goto out_emit;
default:
break;
}
token = g_malloc(sizeof(JSONToken) + input->len + 1);
token->type = type;
memcpy(token->str, input->str, input->len);
token->str[input->len] = 0;
token->x = x;
token->y = y;
parser->token_size += input->len;
g_queue_push_tail(parser->tokens, token);
if (type == JSON_ERROR) {
goto out_emit_bad;
} else if (parser->brace_count < 0 ||
parser->bracket_count < 0 ||
(parser->brace_count == 0 &&
parser->bracket_count == 0)) {
/*
* Security consideration, we limit total memory allocated per object
* and the maximum recursion depth that a message can force.
*/
if (parser->token_size + input->len + 1 > MAX_TOKEN_SIZE) {
error_setg(&err, "JSON token size limit exceeded");
goto out_emit;
}
if (g_queue_get_length(&parser->tokens) + 1 > MAX_TOKEN_COUNT) {
error_setg(&err, "JSON token count limit exceeded");
goto out_emit;
}
if (parser->bracket_count + parser->brace_count > MAX_NESTING) {
error_setg(&err, "JSON nesting depth limit exceeded");
goto out_emit;
} else if (parser->token_size > MAX_TOKEN_SIZE ||
g_queue_get_length(parser->tokens) > MAX_TOKEN_COUNT ||
parser->bracket_count + parser->brace_count > MAX_NESTING) {
/* Security consideration, we limit total memory allocated per object
* and the maximum recursion depth that a message can force.
*/
goto out_emit_bad;
}
return;
token = json_token(type, x, y, input);
parser->token_size += input->len;
g_queue_push_tail(&parser->tokens, token);
if ((parser->brace_count > 0 || parser->bracket_count > 0)
&& parser->bracket_count >= 0 && parser->bracket_count >= 0) {
return;
}
json = json_parser_parse(&parser->tokens, parser->ap, &err);
out_emit_bad:
/*
* Clear out token list and tell the parser to emit an error
* indication by passing it a NULL list
*/
json_message_free_tokens(parser);
out_emit:
/* send current list of tokens to parser and reset tokenizer */
parser->brace_count = 0;
parser->bracket_count = 0;
/* parser->emit takes ownership of parser->tokens. Remove our own
* reference to parser->tokens before handing it out to parser->emit.
*/
tokens = parser->tokens;
parser->tokens = g_queue_new();
parser->emit(parser, tokens);
json_message_free_tokens(parser);
parser->token_size = 0;
parser->emit(parser->opaque, json, err);
}
void json_message_parser_init(JSONMessageParser *parser,
void (*func)(JSONMessageParser *, GQueue *))
void (*emit)(void *opaque, QObject *json,
Error *err),
void *opaque, va_list *ap)
{
parser->emit = func;
parser->emit = emit;
parser->opaque = opaque;
parser->ap = ap;
parser->brace_count = 0;
parser->bracket_count = 0;
parser->tokens = g_queue_new();
g_queue_init(&parser->tokens);
parser->token_size = 0;
json_lexer_init(&parser->lexer, json_message_process_token);
json_lexer_init(&parser->lexer, !!ap);
}
int json_message_parser_feed(JSONMessageParser *parser,
void json_message_parser_feed(JSONMessageParser *parser,
const char *buffer, size_t size)
{
return json_lexer_feed(&parser->lexer, buffer, size);
json_lexer_feed(&parser->lexer, buffer, size);
}
int json_message_parser_flush(JSONMessageParser *parser)
void json_message_parser_flush(JSONMessageParser *parser)
{
return json_lexer_flush(&parser->lexer);
json_lexer_flush(&parser->lexer);
assert(g_queue_is_empty(&parser->tokens));
}
void json_message_parser_destroy(JSONMessageParser *parser)
-1
View File
@@ -13,7 +13,6 @@
#include "qemu/osdep.h"
#include "qapi/qmp/qbool.h"
#include "qemu-common.h"
/**
* qbool_from_bool(): Create a new QBool from a bool
+22 -9
View File
@@ -13,9 +13,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/qmp/json-lexer.h"
#include "qapi/qmp/json-parser.h"
#include "qapi/qmp/json-streamer.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qbool.h"
#include "qapi/qmp/qdict.h"
@@ -27,16 +25,29 @@
typedef struct JSONParsingState
{
JSONMessageParser parser;
va_list *ap;
QObject *result;
Error *err;
} JSONParsingState;
static void parse_json(JSONMessageParser *parser, GQueue *tokens)
static void consume_json(void *opaque, QObject *json, Error *err)
{
JSONParsingState *s = container_of(parser, JSONParsingState, parser);
JSONParsingState *s = opaque;
s->result = json_parser_parse_err(tokens, s->ap, &s->err);
assert(!json != !err);
assert(!s->result || !s->err);
if (s->result) {
qobject_unref(s->result);
s->result = NULL;
error_setg(&s->err, "Expecting at most one JSON value");
}
if (s->err) {
qobject_unref(json);
error_free(err);
return;
}
s->result = json;
s->err = err;
}
/*
@@ -54,13 +65,15 @@ static QObject *qobject_from_jsonv(const char *string, va_list *ap,
{
JSONParsingState state = {};
state.ap = ap;
json_message_parser_init(&state.parser, parse_json);
json_message_parser_init(&state.parser, consume_json, &state, ap);
json_message_parser_feed(&state.parser, string, strlen(string));
json_message_parser_flush(&state.parser);
json_message_parser_destroy(&state.parser);
if (!state.result && !state.err) {
error_setg(&state.err, "Expecting a JSON value");
}
error_propagate(errp, state.err);
return state.result;
}

Some files were not shown because too many files have changed in this diff Show More