From e59f39d40397645477b959255aedfa17a7c9c779 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 23 Aug 2018 18:39:49 +0200 Subject: json: Reject invalid UTF-8 sequences We reject bytes that can't occur in valid UTF-8 (\xC0..\xC1, \xF5..\xFF in the lexer. That's insufficient; there's plenty of invalid UTF-8 not containing these bytes, as demonstrated by check-qjson: * Malformed sequences - Unexpected continuation bytes - Missing continuation bytes after start bytes other than \xC0..\xC1, \xF5..\xFD. * Overlong sequences with start bytes other than \xC0..\xC1, \xF5..\xFD. * Invalid code points Fixing this in the lexer would be bothersome. Fixing it in the parser is straightforward, so do that. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180823164025.12553-23-armbru@redhat.com> --- include/qemu/unicode.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/qemu/unicode.h b/include/qemu/unicode.h index 71c72db461..7fa10b8e60 100644 --- a/include/qemu/unicode.h +++ b/include/qemu/unicode.h @@ -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 -- cgit v1.2.3-55-g7522 From 7c1e1d5481fe8d2e757469179f9ccd14e8838ed1 Mon Sep 17 00:00:00 2001 From: Marc-André Lureau Date: Thu, 23 Aug 2018 18:39:58 +0200 Subject: json: remove useless return value from lexer/parser The lexer always returns 0 when char feeding. Furthermore, none of the caller care about the return value. Signed-off-by: Marc-André Lureau Message-Id: <20180326150916.9602-10-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster Reviewed-by: Thomas Huth Signed-off-by: Markus Armbruster Message-Id: <20180823164025.12553-32-armbru@redhat.com> --- include/qapi/qmp/json-lexer.h | 4 ++-- include/qapi/qmp/json-streamer.h | 4 ++-- qobject/json-lexer.c | 23 ++++++++--------------- qobject/json-streamer.c | 8 ++++---- 4 files changed, 16 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/qapi/qmp/json-lexer.h b/include/qapi/qmp/json-lexer.h index afee7828cd..66ccf0357c 100644 --- a/include/qapi/qmp/json-lexer.h +++ b/include/qapi/qmp/json-lexer.h @@ -47,9 +47,9 @@ struct JSONLexer void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func); -int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size); +void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size); -int json_lexer_flush(JSONLexer *lexer); +void json_lexer_flush(JSONLexer *lexer); void json_lexer_destroy(JSONLexer *lexer); diff --git a/include/qapi/qmp/json-streamer.h b/include/qapi/qmp/json-streamer.h index 00d8a23af8..cb808cf27d 100644 --- a/include/qapi/qmp/json-streamer.h +++ b/include/qapi/qmp/json-streamer.h @@ -36,10 +36,10 @@ typedef struct JSONMessageParser void json_message_parser_init(JSONMessageParser *parser, void (*func)(JSONMessageParser *, GQueue *)); -int json_message_parser_feed(JSONMessageParser *parser, +void json_message_parser_feed(JSONMessageParser *parser, const char *buffer, size_t size); -int json_message_parser_flush(JSONMessageParser *parser); +void json_message_parser_flush(JSONMessageParser *parser); void json_message_parser_destroy(JSONMessageParser *parser); diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index 0731779470..d9701f857b 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -286,7 +286,7 @@ void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func) 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; @@ -340,7 +340,7 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) g_string_truncate(lexer->token, 0); new_state = IN_START; lexer->state = new_state; - return 0; + return; default: break; } @@ -355,29 +355,22 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) g_string_truncate(lexer->token, 0); lexer->state = IN_START; } - - 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 != IN_START) { + json_lexer_feed_char(lexer, 0, true); + } } void json_lexer_destroy(JSONLexer *lexer) diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index c51c2021f9..78dfff2aa0 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -118,15 +118,15 @@ void json_message_parser_init(JSONMessageParser *parser, json_lexer_init(&parser->lexer, json_message_process_token); } -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); } void json_message_parser_destroy(JSONMessageParser *parser) -- cgit v1.2.3-55-g7522 From 037f2440888a22bd00ea0d8e37a1b7ed7d2bba88 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 23 Aug 2018 18:40:00 +0200 Subject: json: Have lexer call streamer directly json_lexer_init() takes the function to process a token as an argument. It's always json_message_process_token(). Makes the code harder to understand for no actual gain. Drop the indirection. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180823164025.12553-34-armbru@redhat.com> --- include/qapi/qmp/json-lexer.h | 13 +++---------- include/qapi/qmp/json-streamer.h | 3 +++ qobject/json-lexer.c | 13 ++++++++----- qobject/json-streamer.c | 6 +++--- 4 files changed, 17 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/qapi/qmp/json-lexer.h b/include/qapi/qmp/json-lexer.h index 66ccf0357c..44bcf2ca64 100644 --- a/include/qapi/qmp/json-lexer.h +++ b/include/qapi/qmp/json-lexer.h @@ -32,20 +32,13 @@ typedef enum json_token_type { JSON_ERROR, } JSONTokenType; -typedef struct JSONLexer JSONLexer; - -typedef void (JSONLexerEmitter)(JSONLexer *, GString *, - JSONTokenType, int x, int y); - -struct JSONLexer -{ - JSONLexerEmitter *emit; +typedef struct JSONLexer { int state; GString *token; int x, y; -}; +} JSONLexer; -void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func); +void json_lexer_init(JSONLexer *lexer); void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size); diff --git a/include/qapi/qmp/json-streamer.h b/include/qapi/qmp/json-streamer.h index cb808cf27d..7922e185a5 100644 --- a/include/qapi/qmp/json-streamer.h +++ b/include/qapi/qmp/json-streamer.h @@ -33,6 +33,9 @@ typedef struct JSONMessageParser uint64_t token_size; } JSONMessageParser; +void json_message_process_token(JSONLexer *lexer, GString *input, + JSONTokenType type, int x, int y); + void json_message_parser_init(JSONMessageParser *parser, void (*func)(JSONMessageParser *, GQueue *)); diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index d9701f857b..17272a3874 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "qapi/qmp/json-lexer.h" +#include "qapi/qmp/json-streamer.h" #define MAX_TOKEN_SIZE (64ULL << 20) @@ -278,9 +279,8 @@ static const uint8_t json_lexer[][256] = { }, }; -void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func) +void json_lexer_init(JSONLexer *lexer) { - lexer->emit = func; lexer->state = IN_START; lexer->token = g_string_sized_new(3); lexer->x = lexer->y = 0; @@ -316,7 +316,8 @@ static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) 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); @@ -336,7 +337,8 @@ static void 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; @@ -351,7 +353,8 @@ static void 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; } diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index 78dfff2aa0..9f57ebf2bd 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -34,8 +34,8 @@ static void json_message_free_tokens(JSONMessageParser *parser) } } -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); JSONToken *token; @@ -115,7 +115,7 @@ void json_message_parser_init(JSONMessageParser *parser, parser->tokens = g_queue_new(); parser->token_size = 0; - json_lexer_init(&parser->lexer, json_message_process_token); + json_lexer_init(&parser->lexer); } void json_message_parser_feed(JSONMessageParser *parser, -- cgit v1.2.3-55-g7522 From 62815d85aed71eff7b6c3a524705180fb04f5d30 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 23 Aug 2018 18:40:01 +0200 Subject: json: Redesign the callback to consume JSON values The classical way to structure parser and lexer is to have the client call the parser to get an abstract syntax tree, the parser call the lexer to get the next token, and the lexer call some function to get input characters. Another way to structure them would be to have the client feed characters to the lexer, the lexer feed tokens to the parser, and the parser feed abstract syntax trees to some callback provided by the client. This way is more easily integrated into an event loop that dispatches input characters as they arrive. Our JSON parser is kind of between the two. The lexer feeds tokens to a "streamer" instead of a real parser. The streamer accumulates tokens until it got the sequence of tokens that comprise a single JSON value (it counts curly braces and square brackets to decide). It feeds those token sequences to a callback provided by the client. The callback passes each token sequence to the parser, and gets back an abstract syntax tree. I figure it was done that way to make a straightforward recursive descent parser possible. "Get next token" becomes "pop the first token off the token sequence". Drawback: we need to store a complete token sequence. Each token eats 13 + input characters + malloc overhead bytes. Observations: 1. This is not the only way to use recursive descent. If we replaced "get next token" by a coroutine yield, we could do without a streamer. 2. The lexer reports errors by passing a JSON_ERROR token to the streamer. This communicates the offending input characters and their location, but no more. 3. The streamer reports errors by passing a null token sequence to the callback. The (already poor) lexical error information is thrown away. 4. Having the callback receive a token sequence duplicates the code to convert token sequence to abstract syntax tree in every callback. 5. Known bug: the streamer silently drops incomplete token sequences. This commit rectifies 4. by lifting the call of the parser from the callbacks into the streamer. Later commits will address 3. and 5. The lifting removes a bug from qjson.c's parse_json(): it passed a pointer to a non-null Error * in certain cases, as demonstrated by check-qjson.c. json_parser_parse() is now unused. It's a stupid wrapper around json_parser_parse_err(). Drop it, and rename json_parser_parse_err() to json_parser_parse(). Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180823164025.12553-35-armbru@redhat.com> --- include/qapi/qmp/json-parser.h | 3 +-- include/qapi/qmp/json-streamer.h | 8 ++++++-- monitor.c | 18 ++++++++---------- qapi/qmp-dispatch.c | 1 - qga/main.c | 12 +++--------- qobject/json-parser.c | 7 +------ qobject/json-streamer.c | 19 +++++++++++-------- qobject/qjson.c | 14 +++++--------- tests/check-qjson.c | 1 - tests/libqtest.c | 10 ++++------ 10 files changed, 39 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/include/qapi/qmp/json-parser.h b/include/qapi/qmp/json-parser.h index 102f5c0068..a34209db7a 100644 --- a/include/qapi/qmp/json-parser.h +++ b/include/qapi/qmp/json-parser.h @@ -16,7 +16,6 @@ #include "qemu-common.h" -QObject *json_parser_parse(GQueue *tokens, va_list *ap); -QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp); +QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp); #endif diff --git a/include/qapi/qmp/json-streamer.h b/include/qapi/qmp/json-streamer.h index 7922e185a5..e162fd01da 100644 --- a/include/qapi/qmp/json-streamer.h +++ b/include/qapi/qmp/json-streamer.h @@ -25,7 +25,9 @@ typedef struct JSONToken { typedef struct JSONMessageParser { - void (*emit)(struct JSONMessageParser *parser, GQueue *tokens); + void (*emit)(void *opaque, QObject *json, Error *err); + void *opaque; + va_list *ap; JSONLexer lexer; int brace_count; int bracket_count; @@ -37,7 +39,9 @@ void json_message_process_token(JSONLexer *lexer, GString *input, JSONTokenType type, int x, int y); void json_message_parser_init(JSONMessageParser *parser, - void (*func)(JSONMessageParser *, GQueue *)); + 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); diff --git a/monitor.c b/monitor.c index 94f673511b..08f799a7bb 100644 --- a/monitor.c +++ b/monitor.c @@ -59,7 +59,6 @@ #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" #include "trace-root.h" @@ -4256,18 +4255,15 @@ 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 */ + /* json_parser_parse() sucks: can fail without setting @err */ error_setg(&err, QERR_JSON_PARSING); } @@ -4465,7 +4461,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 +4680,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 diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 6f2d466596..d8da1a62de 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -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" diff --git a/qga/main.c b/qga/main.c index 87372d40ef..2fc49d00d8 100644 --- a/qga/main.c +++ b/qga/main.c @@ -19,7 +19,6 @@ #include #endif #include "qapi/qmp/json-streamer.h" -#include "qapi/qmp/json-parser.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qstring.h" @@ -597,18 +596,13 @@ 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); if (err) { goto err; } @@ -1320,7 +1314,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()) { diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 7bfa08200c..95fa348e21 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -541,12 +541,7 @@ static QObject *parse_value(JSONParserContext *ctxt, va_list *ap) } } -QObject *json_parser_parse(GQueue *tokens, va_list *ap) -{ - return json_parser_parse_err(tokens, ap, NULL); -} - -QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp) +QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp) { JSONParserContext ctxt = { .buf = tokens }; QObject *result; diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index 9f57ebf2bd..7fd0ff8756 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "qapi/qmp/json-lexer.h" +#include "qapi/qmp/json-parser.h" #include "qapi/qmp/json-streamer.h" #define MAX_TOKEN_SIZE (64ULL << 20) @@ -38,8 +39,9 @@ void json_message_process_token(JSONLexer *lexer, GString *input, JSONTokenType type, int x, int y) { JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer); + Error *err = NULL; JSONToken *token; - GQueue *tokens; + QObject *json; switch (type) { case JSON_LCURLY: @@ -97,19 +99,20 @@ 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; + json = json_parser_parse(parser->tokens, parser->ap, &err); parser->tokens = g_queue_new(); - parser->emit(parser, tokens); 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(); diff --git a/qobject/qjson.c b/qobject/qjson.c index ab4040f235..7395556069 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -13,8 +13,6 @@ #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" @@ -27,16 +25,16 @@ 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); + s->result = json; + error_propagate(&s->err, err); } /* @@ -54,9 +52,7 @@ 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); diff --git a/tests/check-qjson.c b/tests/check-qjson.c index defc21fa04..604886a1a2 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -1438,7 +1438,6 @@ static void multiple_values(void) qobject_unref(obj); /* BUG simultaneously succeeds and fails */ - /* BUG calls json_parser_parse() with errp pointing to non-null */ obj = qobject_from_json("} true", &err); g_assert(qbool_get_bool(qobject_to(QBool, obj))); error_free_or_abort(&err); diff --git a/tests/libqtest.c b/tests/libqtest.c index af2a24e796..1f3b0cb1b1 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -21,9 +21,9 @@ #include #include "libqtest.h" +#include "qemu-common.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "qapi/qmp/json-parser.h" #include "qapi/qmp/json-streamer.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" @@ -446,12 +446,10 @@ typedef struct { QDict *response; } QMPResponseParser; -static void qmp_response(JSONMessageParser *parser, GQueue *tokens) +static void qmp_response(void *opaque, QObject *obj, Error *err) { - QMPResponseParser *qmp = container_of(parser, QMPResponseParser, parser); - QObject *obj; + QMPResponseParser *qmp = opaque; - obj = json_parser_parse(tokens, NULL); if (!obj) { fprintf(stderr, "QMP JSON response parsing failed\n"); abort(); @@ -468,7 +466,7 @@ QDict *qmp_fd_receive(int fd) bool log = getenv("QTEST_LOG") != NULL; qmp.response = NULL; - json_message_parser_init(&qmp.parser, qmp_response); + json_message_parser_init(&qmp.parser, qmp_response, &qmp, NULL); while (!qmp.response) { ssize_t len; char c; -- cgit v1.2.3-55-g7522 From 61030280ca2d67bd63cb068250aee55849cd38ca Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 23 Aug 2018 18:40:04 +0200 Subject: json: Rename token JSON_ESCAPE & friends to JSON_INTERP The JSON parser optionally supports interpolation. The code calls it "escape". Awkward, because it uses the same term for escape sequences within strings. The latter usage is consistent with RFC 8259 "The JavaScript Object Notation (JSON) Data Interchange Format" and ISO C. Call the former "interpolation" instead. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180823164025.12553-38-armbru@redhat.com> --- include/qapi/qmp/json-lexer.h | 2 +- qobject/json-lexer.c | 64 +++++++++++++++++++++---------------------- qobject/json-parser.c | 8 +++--- 3 files changed, 37 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/qapi/qmp/json-lexer.h b/include/qapi/qmp/json-lexer.h index 44bcf2ca64..8bce6ef676 100644 --- a/include/qapi/qmp/json-lexer.h +++ b/include/qapi/qmp/json-lexer.h @@ -27,7 +27,7 @@ typedef enum json_token_type { JSON_FLOAT, JSON_KEYWORD, JSON_STRING, - JSON_ESCAPE, + JSON_INTERP, JSON_SKIP, JSON_ERROR, } JSONTokenType; diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index 17272a3874..5436809be6 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -115,12 +115,12 @@ enum json_lexer_state { IN_NONZERO_NUMBER, IN_NEG_NONZERO_NUMBER, IN_KEYWORD, - IN_ESCAPE, - IN_ESCAPE_L, - IN_ESCAPE_LL, - IN_ESCAPE_I, - IN_ESCAPE_I6, - IN_ESCAPE_I64, + IN_INTERP, + IN_INTERP_L, + IN_INTERP_LL, + IN_INTERP_I, + IN_INTERP_I6, + IN_INTERP_I64, IN_WHITESPACE, IN_START, }; @@ -221,40 +221,40 @@ static const uint8_t json_lexer[][256] = { ['\n'] = IN_WHITESPACE, }, - /* escape */ - [IN_ESCAPE_LL] = { - ['d'] = JSON_ESCAPE, - ['u'] = JSON_ESCAPE, + /* interpolation */ + [IN_INTERP_LL] = { + ['d'] = JSON_INTERP, + ['u'] = JSON_INTERP, }, - [IN_ESCAPE_L] = { - ['d'] = JSON_ESCAPE, - ['l'] = IN_ESCAPE_LL, - ['u'] = JSON_ESCAPE, + [IN_INTERP_L] = { + ['d'] = JSON_INTERP, + ['l'] = IN_INTERP_LL, + ['u'] = JSON_INTERP, }, - [IN_ESCAPE_I64] = { - ['d'] = JSON_ESCAPE, - ['u'] = JSON_ESCAPE, + [IN_INTERP_I64] = { + ['d'] = JSON_INTERP, + ['u'] = JSON_INTERP, }, - [IN_ESCAPE_I6] = { - ['4'] = IN_ESCAPE_I64, + [IN_INTERP_I6] = { + ['4'] = IN_INTERP_I64, }, - [IN_ESCAPE_I] = { - ['6'] = IN_ESCAPE_I6, + [IN_INTERP_I] = { + ['6'] = IN_INTERP_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, + [IN_INTERP] = { + ['d'] = JSON_INTERP, + ['i'] = JSON_INTERP, + ['p'] = JSON_INTERP, + ['s'] = JSON_INTERP, + ['u'] = JSON_INTERP, + ['f'] = JSON_INTERP, + ['l'] = IN_INTERP_L, + ['I'] = IN_INTERP_I, }, /* top level rule */ @@ -271,7 +271,7 @@ static const uint8_t json_lexer[][256] = { [','] = JSON_COMMA, [':'] = JSON_COLON, ['a' ... 'z'] = IN_KEYWORD, - ['%'] = IN_ESCAPE, + ['%'] = IN_INTERP, [' '] = IN_WHITESPACE, ['\t'] = IN_WHITESPACE, ['\r'] = IN_WHITESPACE, @@ -311,7 +311,7 @@ static void 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: diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 06aff19a5d..864cb578d8 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -423,7 +423,7 @@ static QObject *parse_keyword(JSONParserContext *ctxt) return NULL; } -static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap) +static QObject *parse_interpolation(JSONParserContext *ctxt, va_list *ap) { JSONToken *token; @@ -432,7 +432,7 @@ static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap) } token = parser_context_pop_token(ctxt); - assert(token && token->type == JSON_ESCAPE); + assert(token && token->type == JSON_INTERP); if (!strcmp(token->str, "%p")) { return va_arg(*ap, QObject *); @@ -527,8 +527,8 @@ static QObject *parse_value(JSONParserContext *ctxt, va_list *ap) return parse_object(ctxt, ap); case JSON_LSQUARE: return parse_array(ctxt, ap); - case JSON_ESCAPE: - return parse_escape(ctxt, ap); + case JSON_INTERP: + return parse_interpolation(ctxt, ap); case JSON_INTEGER: case JSON_FLOAT: case JSON_STRING: -- cgit v1.2.3-55-g7522 From 2cbd15aa6f4d4694376dd0d231d56e572ac870c1 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 23 Aug 2018 18:40:05 +0200 Subject: json: Treat unwanted interpolation as lexical error The JSON parser optionally supports interpolation. The lexer recognizes interpolation tokens unconditionally. The parser rejects them when interpolation is disabled, in parse_interpolation(). However, it neglects to set an error then, which can make json_parser_parse() fail without setting an error. Move the check for unwanted interpolation from the parser's parse_interpolation() into the lexer's finite state machine. When interpolation is disabled, '%' is now handled like any other unexpected character. The next commit will improve how such lexical errors are handled. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180823164025.12553-39-armbru@redhat.com> --- include/qapi/qmp/json-lexer.h | 4 ++-- qobject/json-lexer.c | 30 ++++++++++++++++++------------ qobject/json-parser.c | 4 ---- qobject/json-streamer.c | 2 +- tests/qmp-test.c | 4 ++++ 5 files changed, 25 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/qapi/qmp/json-lexer.h b/include/qapi/qmp/json-lexer.h index 8bce6ef676..afa84cb910 100644 --- a/include/qapi/qmp/json-lexer.h +++ b/include/qapi/qmp/json-lexer.h @@ -33,12 +33,12 @@ typedef enum json_token_type { } JSONTokenType; typedef struct JSONLexer { - int state; + int start_state, state; GString *token; int x, y; } JSONLexer; -void json_lexer_init(JSONLexer *lexer); +void json_lexer_init(JSONLexer *lexer, bool enable_interpolation); void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size); diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index 5436809be6..96fe13621d 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -92,7 +92,7 @@ * 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: + * - Interpolation, if enabled: * interpolation = %((l|ll|I64)[du]|[ipsf]) * * Note: @@ -123,9 +123,11 @@ enum json_lexer_state { IN_INTERP_I64, 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) @@ -257,8 +259,12 @@ static const uint8_t json_lexer[][256] = { ['I'] = IN_INTERP_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, @@ -271,17 +277,18 @@ static const uint8_t json_lexer[][256] = { [','] = JSON_COMMA, [':'] = JSON_COLON, ['a' ... 'z'] = IN_KEYWORD, - ['%'] = IN_INTERP, [' '] = IN_WHITESPACE, ['\t'] = IN_WHITESPACE, ['\r'] = IN_WHITESPACE, ['\n'] = IN_WHITESPACE, }, + [IN_START_INTERP]['%'] = IN_INTERP, }; -void json_lexer_init(JSONLexer *lexer) +void json_lexer_init(JSONLexer *lexer, bool enable_interpolation) { - 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; } @@ -321,7 +328,7 @@ static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) /* 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 @@ -340,8 +347,7 @@ static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) 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; + lexer->state = lexer->start_state; return; default: break; @@ -356,7 +362,7 @@ static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) 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; } } @@ -371,7 +377,7 @@ void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size) void json_lexer_flush(JSONLexer *lexer) { - if (lexer->state != IN_START) { + if (lexer->state != lexer->start_state) { json_lexer_feed_char(lexer, 0, true); } } diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 864cb578d8..2855eaaeca 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -427,10 +427,6 @@ static QObject *parse_interpolation(JSONParserContext *ctxt, va_list *ap) { JSONToken *token; - if (ap == NULL) { - return NULL; - } - token = parser_context_pop_token(ctxt); assert(token && token->type == JSON_INTERP); diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index fa595a8761..a373e0114a 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -115,7 +115,7 @@ void json_message_parser_init(JSONMessageParser *parser, parser->tokens = g_queue_new(); parser->token_size = 0; - json_lexer_init(&parser->lexer); + json_lexer_init(&parser->lexer, !!ap); } void json_message_parser_feed(JSONMessageParser *parser, diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 7b3ba17c4a..4ae2245484 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -94,6 +94,10 @@ static void test_malformed(QTestState *qts) /* lexical error: interpolation */ qtest_qmp_send_raw(qts, "%%p\n"); + /* two errors, one for "%", one for "p" */ + resp = qtest_qmp_receive(qts); + g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); + qobject_unref(resp); resp = qtest_qmp_receive(qts); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); qobject_unref(resp); -- cgit v1.2.3-55-g7522 From 84a56f38b23440cb3127eaffe4e495826a29f18c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 23 Aug 2018 18:40:06 +0200 Subject: json: Pass lexical errors and limit violations to callback The callback to consume JSON values takes QObject *json, Error *err. If both are null, the callback is supposed to make up an error by itself. This sucks. qjson.c's consume_json() neglects to do so, which makes qobject_from_json() null instead of failing. I consider that a bug. The culprit is json_message_process_token(): it passes two null pointers when it runs into a lexical error or a limit violation. Fix it to pass a proper Error object then. Update the callbacks: * monitor.c's handle_qmp_command(): the code to make up an error is now dead, drop it. * qga/main.c's process_event(): lumps the "both null" case together with the "not a JSON object" case. The former is now gone. The error message "Invalid JSON syntax" is misleading for the latter. Improve it to "Input must be a JSON object". * qobject/qjson.c's consume_json(): no update; check-qjson demonstrates qobject_from_json() now sets an error on lexical errors, but still doesn't on some other errors. * tests/libqtest.c's qmp_response(): the Error object is now reliable, so use it to improve the error message. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180823164025.12553-40-armbru@redhat.com> --- include/qapi/qmp/qerror.h | 3 --- monitor.c | 5 +---- qga/main.c | 3 ++- qobject/json-lexer.c | 3 +-- qobject/json-streamer.c | 22 ++++++++++++++++------ tests/check-qjson.c | 15 ++++++++------- tests/libqtest.c | 7 +++++-- 7 files changed, 33 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index c82360f429..145571f618 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -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" diff --git a/monitor.c b/monitor.c index 08f799a7bb..3dbdcb5190 100644 --- a/monitor.c +++ b/monitor.c @@ -4262,10 +4262,7 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err) QDict *qdict; QMPRequest *req_obj; - if (!req && !err) { - /* json_parser_parse() sucks: can fail without setting @err */ - error_setg(&err, QERR_JSON_PARSING); - } + assert(!req != !err); qdict = qobject_to(QDict, req); if (qdict) { diff --git a/qga/main.c b/qga/main.c index 2fc49d00d8..b74e1241ef 100644 --- a/qga/main.c +++ b/qga/main.c @@ -603,12 +603,13 @@ static void process_event(void *opaque, QObject *obj, Error *err) int ret; g_debug("process_event: called"); + 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")) { diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index 96fe13621d..7c31c2c8ff 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -334,8 +334,7 @@ static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) /* 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) diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index a373e0114a..e372ecc895 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" +#include "qapi/error.h" #include "qapi/qmp/json-lexer.h" #include "qapi/qmp/json-parser.h" #include "qapi/qmp/json-streamer.h" @@ -57,6 +58,7 @@ void json_message_process_token(JSONLexer *lexer, GString *input, parser->bracket_count--; break; case JSON_ERROR: + error_setg(&err, "JSON parse error, stray '%s'", input->str); goto out_emit; default: break; @@ -82,12 +84,20 @@ void json_message_process_token(JSONLexer *lexer, GString *input, goto out_emit; } - 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. - */ + /* + * Security consideration, we limit total memory allocated per object + * and the maximum recursion depth that a message can force. + */ + if (parser->token_size > MAX_TOKEN_SIZE) { + error_setg(&err, "JSON token size limit exceeded"); + goto out_emit; + } + if (g_queue_get_length(parser->tokens) > 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; } diff --git a/tests/check-qjson.c b/tests/check-qjson.c index 604886a1a2..d6fda0786f 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -1021,6 +1021,7 @@ static void interpolation_unknown(void) } g_test_trap_subprocess(NULL, 0, 0); g_test_trap_assert_failed(); + g_test_trap_assert_stderr("*Unexpected error*stray '%x'*"); } static void interpolation_string(void) @@ -1296,11 +1297,11 @@ static void junk_input(void) QObject *obj; obj = qobject_from_json("@", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); obj = qobject_from_json("{\x01", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); obj = qobject_from_json("[0\xFF]", &err); @@ -1308,11 +1309,11 @@ static void junk_input(void) g_assert(obj == NULL); obj = qobject_from_json("00", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); obj = qobject_from_json("[1e", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); obj = qobject_from_json("truer", &err); @@ -1324,7 +1325,7 @@ static void unterminated_string(void) { Error *err = NULL; QObject *obj = qobject_from_json("\"abc", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1332,7 +1333,7 @@ static void unterminated_sq_string(void) { Error *err = NULL; QObject *obj = qobject_from_json("'abc", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1340,7 +1341,7 @@ static void unterminated_escape(void) { Error *err = NULL; QObject *obj = qobject_from_json("\"abc\\\"", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } diff --git a/tests/libqtest.c b/tests/libqtest.c index 1f3b0cb1b1..5973a67652 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -450,8 +450,11 @@ static void qmp_response(void *opaque, QObject *obj, Error *err) { QMPResponseParser *qmp = opaque; - if (!obj) { - fprintf(stderr, "QMP JSON response parsing failed\n"); + assert(!obj != !err); + + if (err) { + error_prepend(&err, "QMP JSON response parsing failed: "); + error_report_err(err); abort(); } -- cgit v1.2.3-55-g7522 From f9277915ee7b2654f5347c4c261c8a0651fdd561 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 23 Aug 2018 18:40:12 +0200 Subject: json: Fix streamer not to ignore trailing unterminated structures json_message_process_token() accumulates tokens until it got the sequence of tokens that comprise a single JSON value (it counts curly braces and square brackets to decide). It feeds those token sequences to json_parser_parse(). If a non-empty sequence of tokens remains at the end of the parse, it's silently ignored. check-qjson.c cases unterminated_array(), unterminated_array_comma(), unterminated_dict(), unterminated_dict_comma() demonstrate this bug. Fix as follows. Introduce a JSON_END_OF_INPUT token. When the streamer receives it, it feeds the accumulated tokens to json_parser_parse(). Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180823164025.12553-46-armbru@redhat.com> --- include/qapi/qmp/json-lexer.h | 1 + qobject/json-lexer.c | 2 ++ qobject/json-streamer.c | 8 ++++++++ tests/check-qjson.c | 8 ++++---- 4 files changed, 15 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/qapi/qmp/json-lexer.h b/include/qapi/qmp/json-lexer.h index afa84cb910..508fc7bdaf 100644 --- a/include/qapi/qmp/json-lexer.h +++ b/include/qapi/qmp/json-lexer.h @@ -30,6 +30,7 @@ typedef enum json_token_type { JSON_INTERP, JSON_SKIP, JSON_ERROR, + JSON_END_OF_INPUT, } JSONTokenType; typedef struct JSONLexer { diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index 01417dca9d..a728c32faa 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -347,6 +347,8 @@ void json_lexer_flush(JSONLexer *lexer) 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) diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index e372ecc895..674dfe6e85 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -60,6 +60,13 @@ void json_message_process_token(JSONLexer *lexer, GString *input, 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); + parser->tokens = NULL; + goto out_emit; default: break; } @@ -137,6 +144,7 @@ void json_message_parser_feed(JSONMessageParser *parser, void json_message_parser_flush(JSONMessageParser *parser) { json_lexer_flush(&parser->lexer); + assert(g_queue_is_empty(parser->tokens)); } void json_message_parser_destroy(JSONMessageParser *parser) diff --git a/tests/check-qjson.c b/tests/check-qjson.c index f9438370d9..0ca4b3c823 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -1360,7 +1360,7 @@ static void unterminated_array(void) { Error *err = NULL; QObject *obj = qobject_from_json("[32", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1368,7 +1368,7 @@ static void unterminated_array_comma(void) { Error *err = NULL; QObject *obj = qobject_from_json("[32,", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1384,7 +1384,7 @@ static void unterminated_dict(void) { Error *err = NULL; QObject *obj = qobject_from_json("{'abc':32", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1392,7 +1392,7 @@ static void unterminated_dict_comma(void) { Error *err = NULL; QObject *obj = qobject_from_json("{'abc':32,", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } -- cgit v1.2.3-55-g7522 From a2731e08ee8633fcdc2af944b8f8f315678f7669 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 23 Aug 2018 18:40:17 +0200 Subject: json: Unbox tokens queue in JSONMessageParser Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180823164025.12553-51-armbru@redhat.com> --- include/qapi/qmp/json-streamer.h | 2 +- qobject/json-parser.c | 1 - qobject/json-streamer.c | 30 +++++++++++------------------- 3 files changed, 12 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/qapi/qmp/json-streamer.h b/include/qapi/qmp/json-streamer.h index e162fd01da..d1d7fe2595 100644 --- a/include/qapi/qmp/json-streamer.h +++ b/include/qapi/qmp/json-streamer.h @@ -31,7 +31,7 @@ typedef struct JSONMessageParser JSONLexer lexer; int brace_count; int bracket_count; - GQueue *tokens; + GQueue tokens; uint64_t token_size; } JSONMessageParser; diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 685e9dac24..e9a9f937f3 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -552,7 +552,6 @@ QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp) parser_context_pop_token(&ctxt); } g_free(ctxt.current); - g_queue_free(ctxt.buf); return result; } diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index 954bf9d468..9210281a65 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -22,17 +22,12 @@ #define MAX_TOKEN_COUNT (2ULL << 20) #define MAX_NESTING (1 << 10) -static void json_message_free_token(void *token, void *opaque) -{ - g_free(token); -} - 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); } } @@ -61,11 +56,10 @@ void json_message_process_token(JSONLexer *lexer, GString *input, 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)) { + if (g_queue_is_empty(&parser->tokens)) { return; } - json = json_parser_parse(parser->tokens, parser->ap, &err); - parser->tokens = NULL; + json = json_parser_parse(&parser->tokens, parser->ap, &err); goto out_emit; default: break; @@ -79,7 +73,7 @@ void json_message_process_token(JSONLexer *lexer, GString *input, error_setg(&err, "JSON token size limit exceeded"); goto out_emit; } - if (g_queue_get_length(parser->tokens) + 1 > MAX_TOKEN_COUNT) { + if (g_queue_get_length(&parser->tokens) + 1 > MAX_TOKEN_COUNT) { error_setg(&err, "JSON token count limit exceeded"); goto out_emit; } @@ -97,21 +91,19 @@ void json_message_process_token(JSONLexer *lexer, GString *input, parser->token_size += input->len; - g_queue_push_tail(parser->tokens, token); + 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); - parser->tokens = NULL; + json = json_parser_parse(&parser->tokens, parser->ap, &err); out_emit: parser->brace_count = 0; parser->bracket_count = 0; json_message_free_tokens(parser); - parser->tokens = g_queue_new(); parser->token_size = 0; parser->emit(parser->opaque, json, err); } @@ -126,7 +118,7 @@ void json_message_parser_init(JSONMessageParser *parser, 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, !!ap); @@ -141,7 +133,7 @@ void json_message_parser_feed(JSONMessageParser *parser, void json_message_parser_flush(JSONMessageParser *parser) { json_lexer_flush(&parser->lexer); - assert(g_queue_is_empty(parser->tokens)); + assert(g_queue_is_empty(&parser->tokens)); } void json_message_parser_destroy(JSONMessageParser *parser) -- cgit v1.2.3-55-g7522 From abe7c2067c21a89c6fd4cf2d8ba2fa37160d1d55 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 23 Aug 2018 18:40:18 +0200 Subject: json: Make JSONToken opaque outside json-parser.c Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180823164025.12553-52-armbru@redhat.com> --- include/qapi/qmp/json-parser.h | 4 ++++ include/qapi/qmp/json-streamer.h | 7 ------- qobject/json-parser.c | 19 +++++++++++++++++++ qobject/json-streamer.c | 8 +------- 4 files changed, 24 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/qapi/qmp/json-parser.h b/include/qapi/qmp/json-parser.h index a34209db7a..21b23d7bec 100644 --- a/include/qapi/qmp/json-parser.h +++ b/include/qapi/qmp/json-parser.h @@ -15,7 +15,11 @@ #define QEMU_JSON_PARSER_H #include "qemu-common.h" +#include "qapi/qmp/json-lexer.h" +typedef struct JSONToken JSONToken; + +JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr); QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp); #endif diff --git a/include/qapi/qmp/json-streamer.h b/include/qapi/qmp/json-streamer.h index d1d7fe2595..29950ac37c 100644 --- a/include/qapi/qmp/json-streamer.h +++ b/include/qapi/qmp/json-streamer.h @@ -16,13 +16,6 @@ #include "qapi/qmp/json-lexer.h" -typedef struct JSONToken { - int type; - int x; - int y; - char str[]; -} JSONToken; - typedef struct JSONMessageParser { void (*emit)(void *opaque, QObject *json, Error *err); diff --git a/qobject/json-parser.c b/qobject/json-parser.c index e9a9f937f3..a247875f14 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -26,6 +26,13 @@ #include "qapi/qmp/json-lexer.h" #include "qapi/qmp/json-streamer.h" +struct JSONToken { + JSONTokenType type; + int x; + int y; + char str[]; +}; + typedef struct JSONParserContext { Error *err; @@ -538,6 +545,18 @@ static QObject *parse_value(JSONParserContext *ctxt, va_list *ap) } } +JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr) +{ + JSONToken *token = g_malloc(sizeof(JSONToken) + tokstr->len + 1); + + token->type = type; + memcpy(token->str, tokstr->str, tokstr->len); + token->str[tokstr->len] = 0; + token->x = x; + token->y = y; + return token; +} + QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp) { JSONParserContext ctxt = { .buf = tokens }; diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index 9210281a65..467bc29413 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -82,13 +82,7 @@ void json_message_process_token(JSONLexer *lexer, GString *input, goto out_emit; } - 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; - + token = json_token(type, x, y, input); parser->token_size += input->len; g_queue_push_tail(&parser->tokens, token); -- cgit v1.2.3-55-g7522 From 812ce33eadfc419795a5d1c7fe8f487fdfe2188c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 23 Aug 2018 18:40:19 +0200 Subject: qobject: Drop superfluous includes of qemu-common.h Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180823164025.12553-53-armbru@redhat.com> --- include/qapi/qmp/json-parser.h | 1 - qobject/json-lexer.c | 1 - qobject/json-streamer.c | 1 - qobject/qbool.c | 1 - qobject/qlist.c | 1 - qobject/qnull.c | 1 - qobject/qnum.c | 1 - qobject/qobject.c | 1 - qobject/qstring.c | 1 - 9 files changed, 9 deletions(-) (limited to 'include') diff --git a/include/qapi/qmp/json-parser.h b/include/qapi/qmp/json-parser.h index 21b23d7bec..55f75954c3 100644 --- a/include/qapi/qmp/json-parser.h +++ b/include/qapi/qmp/json-parser.h @@ -14,7 +14,6 @@ #ifndef QEMU_JSON_PARSER_H #define QEMU_JSON_PARSER_H -#include "qemu-common.h" #include "qapi/qmp/json-lexer.h" typedef struct JSONToken JSONToken; diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index a728c32faa..06ec67dc45 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -12,7 +12,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/qmp/json-lexer.h" #include "qapi/qmp/json-streamer.h" diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index 467bc29413..da53e770e9 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -12,7 +12,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "qapi/qmp/json-lexer.h" #include "qapi/qmp/json-parser.h" diff --git a/qobject/qbool.c b/qobject/qbool.c index b58249925c..06dfc43498 100644 --- a/qobject/qbool.c +++ b/qobject/qbool.c @@ -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 diff --git a/qobject/qlist.c b/qobject/qlist.c index 37c1c167f1..b3274af88b 100644 --- a/qobject/qlist.c +++ b/qobject/qlist.c @@ -17,7 +17,6 @@ #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qemu/queue.h" -#include "qemu-common.h" /** * qlist_new(): Create a new QList diff --git a/qobject/qnull.c b/qobject/qnull.c index f6f55f11ea..00870a1824 100644 --- a/qobject/qnull.c +++ b/qobject/qnull.c @@ -11,7 +11,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/qmp/qnull.h" QNull qnull_ = { diff --git a/qobject/qnum.c b/qobject/qnum.c index 1501c82832..7012fc57f2 100644 --- a/qobject/qnum.c +++ b/qobject/qnum.c @@ -14,7 +14,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qnum.h" -#include "qemu-common.h" /** * qnum_from_int(): Create a new QNum from an int64_t diff --git a/qobject/qobject.c b/qobject/qobject.c index cf4b7e229e..878dd76e79 100644 --- a/qobject/qobject.c +++ b/qobject/qobject.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qnull.h" #include "qapi/qmp/qnum.h" diff --git a/qobject/qstring.c b/qobject/qstring.c index 0f1510e792..1c6897df00 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qstring.h" -#include "qemu-common.h" /** * qstring_new(): Create a new empty QString -- cgit v1.2.3-55-g7522 From 86cdf9ec8dec2763702cc52fa412d108a5dc9608 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 23 Aug 2018 18:40:20 +0200 Subject: json: Clean up headers The JSON parser has three public headers, json-lexer.h, json-parser.h, json-streamer.h. They all contain stuff that is of no interest outside qobject/json-*.c. Collect the public interface in include/qapi/qmp/json-parser.h, and everything else in qobject/json-parser-int.h. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20180823164025.12553-54-armbru@redhat.com> --- include/qapi/qmp/json-lexer.h | 50 ------------------------------------- include/qapi/qmp/json-parser.h | 36 +++++++++++++++++++++------ include/qapi/qmp/json-streamer.h | 46 ---------------------------------- monitor.c | 2 +- qga/main.c | 2 +- qobject/json-lexer.c | 3 +-- qobject/json-parser-int.h | 54 ++++++++++++++++++++++++++++++++++++++++ qobject/json-parser.c | 4 +-- qobject/json-streamer.c | 4 +-- qobject/qjson.c | 2 +- tests/libqtest.c | 2 +- 11 files changed, 90 insertions(+), 115 deletions(-) delete mode 100644 include/qapi/qmp/json-lexer.h delete mode 100644 include/qapi/qmp/json-streamer.h create mode 100644 qobject/json-parser-int.h (limited to 'include') diff --git a/include/qapi/qmp/json-lexer.h b/include/qapi/qmp/json-lexer.h deleted file mode 100644 index 508fc7bdaf..0000000000 --- a/include/qapi/qmp/json-lexer.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * JSON lexer - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori - * - * 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_INTERP, - JSON_SKIP, - JSON_ERROR, - JSON_END_OF_INPUT, -} JSONTokenType; - -typedef struct JSONLexer { - int start_state, state; - GString *token; - int x, y; -} JSONLexer; - -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); - -#endif diff --git a/include/qapi/qmp/json-parser.h b/include/qapi/qmp/json-parser.h index 55f75954c3..7345a9bd5c 100644 --- a/include/qapi/qmp/json-parser.h +++ b/include/qapi/qmp/json-parser.h @@ -1,5 +1,5 @@ /* - * JSON Parser + * JSON Parser * * Copyright IBM, Corp. 2009 * @@ -11,14 +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 "qapi/qmp/json-lexer.h" +typedef struct JSONLexer { + int start_state, state; + GString *token; + int x, y; +} JSONLexer; -typedef struct JSONToken JSONToken; +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; -JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr); -QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp); +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 diff --git a/include/qapi/qmp/json-streamer.h b/include/qapi/qmp/json-streamer.h deleted file mode 100644 index 29950ac37c..0000000000 --- a/include/qapi/qmp/json-streamer.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * JSON streaming support - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori - * - * 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 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_process_token(JSONLexer *lexer, GString *input, - JSONTokenType type, int x, int y); - -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 diff --git a/monitor.c b/monitor.c index 3dbdcb5190..021c11b1bf 100644 --- a/monitor.c +++ b/monitor.c @@ -58,7 +58,7 @@ #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" #include "trace-root.h" diff --git a/qga/main.c b/qga/main.c index b74e1241ef..6d70242d05 100644 --- a/qga/main.c +++ b/qga/main.c @@ -18,7 +18,7 @@ #include #include #endif -#include "qapi/qmp/json-streamer.h" +#include "qapi/qmp/json-parser.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qstring.h" diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index 06ec67dc45..e1745a3d95 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -12,8 +12,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/json-lexer.h" -#include "qapi/qmp/json-streamer.h" +#include "json-parser-int.h" #define MAX_TOKEN_SIZE (64ULL << 20) diff --git a/qobject/json-parser-int.h b/qobject/json-parser-int.h new file mode 100644 index 0000000000..ceaa890ec6 --- /dev/null +++ b/qobject/json-parser-int.h @@ -0,0 +1,54 @@ +/* + * JSON Parser + * + * Copyright IBM, Corp. 2009 + * + * Authors: + * Anthony Liguori + * + * 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 diff --git a/qobject/json-parser.c b/qobject/json-parser.c index a247875f14..7449684f1c 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -22,9 +22,7 @@ #include "qapi/qmp/qnull.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/json-lexer.h" -#include "qapi/qmp/json-streamer.h" +#include "json-parser-int.h" struct JSONToken { JSONTokenType type; diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index da53e770e9..47dd7ea576 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -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 "json-parser-int.h" #define MAX_TOKEN_SIZE (64ULL << 20) #define MAX_TOKEN_COUNT (2ULL << 20) diff --git a/qobject/qjson.c b/qobject/qjson.c index b9ccae2c2a..db36101f3b 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/json-streamer.h" +#include "qapi/qmp/json-parser.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" diff --git a/tests/libqtest.c b/tests/libqtest.c index 5973a67652..d635c5bea0 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -24,7 +24,7 @@ #include "qemu-common.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "qapi/qmp/json-streamer.h" +#include "qapi/qmp/json-parser.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" -- cgit v1.2.3-55-g7522 From 37aded92c27d0e56cd27f1c29494fc9f8c873cdd Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 23 Aug 2018 18:40:25 +0200 Subject: json: Update references to RFC 7159 to RFC 8259 RFC 8259 (December 2017) obsoletes RFC 7159 (March 2014). Signed-off-by: Markus Armbruster Message-Id: <20180823164025.12553-59-armbru@redhat.com> Reviewed-by: Eric Blake --- include/qapi/qmp/qnum.h | 2 +- qapi/introspect.json | 2 +- qobject/json-parser.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/qapi/qmp/qnum.h b/include/qapi/qmp/qnum.h index 45bf02a036..bbae0a5ec8 100644 --- a/include/qapi/qmp/qnum.h +++ b/include/qapi/qmp/qnum.h @@ -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 diff --git a/qapi/introspect.json b/qapi/introspect.json index 137b39b992..3d22166b2b 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -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'. # diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 3318b8dad0..5a840dfd86 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -516,7 +516,7 @@ static QObject *parse_literal(JSONParserContext *ctxt) } case JSON_FLOAT: /* FIXME dependent on locale; a pervasive issue in QEMU */ - /* FIXME our lexer matches RFC 7159 in forbidding Inf or NaN, + /* FIXME our lexer matches RFC 8259 in forbidding Inf or NaN, * but those might be useful extensions beyond JSON */ return QOBJECT(qnum_from_double(strtod(token->str, NULL))); default: -- cgit v1.2.3-55-g7522