Commit 19c04b99ca for qemu.org

commit 19c04b99cad2aca987cda28671b1e959fc2cb6f7
Author: Paolo Bonzini <pbonzini@redhat.com>
Date:   Fri Jun 26 12:17:23 2026 +0200

    json-streamer: make brace/bracket count unsigned

    It makes no sense to let brace_count and bracket_count go negative,
    also because it immediately ends error recovery and sets them both
    back to zero.  Instead set them to zero *before* choosing
    whether to process the token queue; this makes it possible to
    have the fields as unsigned.

    Note that JSON_END_OF_INPUT now forces the parentheses to appear
    balanced, so that the queue is emptied and an error is reported;
    hence, the "type != JSON_END_OF_INPUT" condition can be removed.

    Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
    Message-ID: <20260626101727.1727389-4-pbonzini@redhat.com>
    Reviewed-by: Markus Armbruster <armbru@redhat.com>
    [Comment tweaked]
    Signed-off-by: Markus Armbruster <armbru@redhat.com>

diff --git a/include/qobject/json-parser.h b/include/qobject/json-parser.h
index 4c3d89f751..0cf6932ecd 100644
--- a/include/qobject/json-parser.h
+++ b/include/qobject/json-parser.h
@@ -31,8 +31,8 @@ typedef struct JSONMessageParser {
     void *opaque;
     JSONLexer lexer;
     JSONParserContext parser;
-    int brace_count;
-    int bracket_count;
+    unsigned int brace_count;
+    unsigned int bracket_count;
     GQueue tokens;
     uint64_t token_size;
 } JSONMessageParser;
diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c
index 6c4f99b3e7..e18fd9bb0e 100644
--- a/qobject/json-streamer.c
+++ b/qobject/json-streamer.c
@@ -41,21 +41,41 @@ void json_message_process_token(JSONLexer *lexer, GString *input,
         parser->brace_count++;
         break;
     case JSON_RCURLY:
+        if (!parser->brace_count) {
+            goto end_error_recovery;
+        }
         parser->brace_count--;
         break;
     case JSON_LSQUARE:
         parser->bracket_count++;
         break;
     case JSON_RSQUARE:
+        if (!parser->bracket_count) {
+            goto end_error_recovery;
+        }
         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:
+        /*
+         * Force the parentheses to appear balanced and the queue
+         * to be emptied, causing a parse error if it wasn't.
+         */
         if (g_queue_is_empty(&parser->tokens)) {
             return;
         }
+    end_error_recovery:
+        /*
+         * We come here due to receiving either JSON_ERROR or a
+         * JSON_R{CURLY,SQUARE}) that is known to be unbalanced.
+         * If in error recovery, end it immediately.  If not in
+         * error recovery, json_parser_feed() will raise an error
+         * but error recovery won't be entered at all.
+         */
+        parser->brace_count = 0;
+        parser->bracket_count = 0;
         break;
     default:
         break;
@@ -83,9 +103,7 @@ void json_message_process_token(JSONLexer *lexer, GString *input,

     g_queue_push_tail(&parser->tokens, token);

-    if ((parser->brace_count > 0 || parser->bracket_count > 0)
-        && parser->brace_count >= 0 && parser->bracket_count >= 0
-        && type != JSON_END_OF_INPUT) {
+    if (parser->brace_count > 0 || parser->bracket_count > 0) {
         return;
     }