summary refs log tree commit diff stats
path: root/qobject/json-streamer.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2018-08-25 10:11:54 +0100
committerPeter Maydell <peter.maydell@linaro.org>2018-08-25 10:11:54 +0100
commitcc9821fa9ac43728a7ece0a5e42e0147e6aadbf4 (patch)
treecdeb23078fab646764f9c0ee38bca585d8be0f89 /qobject/json-streamer.c
parente2e6fa67931fdba493e10cc55abcc99a65c92c7b (diff)
parent37aded92c27d0e56cd27f1c29494fc9f8c873cdd (diff)
downloadfocaccia-qemu-cc9821fa9ac43728a7ece0a5e42e0147e6aadbf4.tar.gz
focaccia-qemu-cc9821fa9ac43728a7ece0a5e42e0147e6aadbf4.zip
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>
Diffstat (limited to 'qobject/json-streamer.c')
-rw-r--r--qobject/json-streamer.c116
1 files changed, 57 insertions, 59 deletions
diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c
index c51c2021f9..47dd7ea576 100644
--- a/qobject/json-streamer.c
+++ b/qobject/json-streamer.c
@@ -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;
+    /*
+     * 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;
+    }
 
+    token = json_token(type, x, y, input);
     parser->token_size += input->len;
 
-    g_queue_push_tail(parser->tokens, token);
+    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)) {
-        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;
+    if ((parser->brace_count > 0 || parser->bracket_count > 0)
+        && parser->bracket_count >= 0 && parser->bracket_count >= 0) {
+        return;
     }
 
-    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)