xref: /openbmc/qemu/qobject/json-streamer.c (revision 2e1cacfb)
1 /*
2  * JSON streaming support
3  *
4  * Copyright IBM, Corp. 2009
5  *
6  * Authors:
7  *  Anthony Liguori   <aliguori@us.ibm.com>
8  *
9  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10  * See the COPYING.LIB file in the top-level directory.
11  *
12  */
13 
14 #include "qemu/osdep.h"
15 #include "qapi/error.h"
16 #include "json-parser-int.h"
17 
18 #define MAX_TOKEN_SIZE (64ULL << 20)
19 #define MAX_TOKEN_COUNT (2ULL << 20)
20 #define MAX_NESTING (1 << 10)
21 
22 static void json_message_free_tokens(JSONMessageParser *parser)
23 {
24     JSONToken *token;
25 
26     while ((token = g_queue_pop_head(&parser->tokens))) {
27         g_free(token);
28     }
29 }
30 
31 void json_message_process_token(JSONLexer *lexer, GString *input,
32                                 JSONTokenType type, int x, int y)
33 {
34     JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer);
35     QObject *json = NULL;
36     Error *err = NULL;
37     JSONToken *token;
38 
39     switch (type) {
40     case JSON_LCURLY:
41         parser->brace_count++;
42         break;
43     case JSON_RCURLY:
44         parser->brace_count--;
45         break;
46     case JSON_LSQUARE:
47         parser->bracket_count++;
48         break;
49     case JSON_RSQUARE:
50         parser->bracket_count--;
51         break;
52     case JSON_ERROR:
53         error_setg(&err, "JSON parse error, stray '%s'", input->str);
54         goto out_emit;
55     case JSON_END_OF_INPUT:
56         if (g_queue_is_empty(&parser->tokens)) {
57             return;
58         }
59         json = json_parser_parse(&parser->tokens, parser->ap, &err);
60         goto out_emit;
61     default:
62         break;
63     }
64 
65     /*
66      * Security consideration, we limit total memory allocated per object
67      * and the maximum recursion depth that a message can force.
68      */
69     if (parser->token_size + input->len + 1 > MAX_TOKEN_SIZE) {
70         error_setg(&err, "JSON token size limit exceeded");
71         goto out_emit;
72     }
73     if (g_queue_get_length(&parser->tokens) + 1 > MAX_TOKEN_COUNT) {
74         error_setg(&err, "JSON token count limit exceeded");
75         goto out_emit;
76     }
77     if (parser->bracket_count + parser->brace_count > MAX_NESTING) {
78         error_setg(&err, "JSON nesting depth limit exceeded");
79         goto out_emit;
80     }
81 
82     token = json_token(type, x, y, input);
83     parser->token_size += input->len;
84 
85     g_queue_push_tail(&parser->tokens, token);
86 
87     if ((parser->brace_count > 0 || parser->bracket_count > 0)
88         && parser->brace_count >= 0 && parser->bracket_count >= 0) {
89         return;
90     }
91 
92     json = json_parser_parse(&parser->tokens, parser->ap, &err);
93 
94 out_emit:
95     parser->brace_count = 0;
96     parser->bracket_count = 0;
97     json_message_free_tokens(parser);
98     parser->token_size = 0;
99     parser->emit(parser->opaque, json, err);
100 }
101 
102 void json_message_parser_init(JSONMessageParser *parser,
103                               void (*emit)(void *opaque, QObject *json,
104                                            Error *err),
105                               void *opaque, va_list *ap)
106 {
107     parser->emit = emit;
108     parser->opaque = opaque;
109     parser->ap = ap;
110     parser->brace_count = 0;
111     parser->bracket_count = 0;
112     g_queue_init(&parser->tokens);
113     parser->token_size = 0;
114 
115     json_lexer_init(&parser->lexer, !!ap);
116 }
117 
118 void json_message_parser_feed(JSONMessageParser *parser,
119                              const char *buffer, size_t size)
120 {
121     json_lexer_feed(&parser->lexer, buffer, size);
122 }
123 
124 void json_message_parser_flush(JSONMessageParser *parser)
125 {
126     json_lexer_flush(&parser->lexer);
127     assert(g_queue_is_empty(&parser->tokens));
128 }
129 
130 void json_message_parser_destroy(JSONMessageParser *parser)
131 {
132     json_lexer_destroy(&parser->lexer);
133     json_message_free_tokens(parser);
134 }
135