QMP and QObject patches

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJWVsvGAAoJEDhwtADrkYZTtnwP+wcrBt/uqw3Q0k5OC4vwDa6z
 OYd8KVK60PozjbRDkiaZE7Iqu9eo47rbRCfTyta7/QFP49/ERVja7+QcFO4681Sj
 ELFQKCztU39lu8kNm4/+WYRdcTUKVTY2JQmz/d4qEDBD0EKGPnQ+r6PiCr5jS4Qn
 IScyU4dzVuNhkpzuphuJuoBnvPsnhlwNPtDkSZ1dV7At3zx52mQAlzjJID52Hr4K
 FIReQlhXQognZb3AtYrFilK/BMOA7Vj/HO5apzf72OkdL5lKYuSeSbxV/rqWB5jh
 tPyzv5Ufi6cZ+eNTQ6aDy/O8+9ktu5sAqpi4mX9IMwKsIdxu1GuqX+9MW8ecuE7M
 IOA8Clj17PjUPHKgExh29i3W32zxjryWpZAcRyNEf4ovUnbMPAYyAdEctTTTChGE
 sthkFBpqEflaEZPYIjsAMyViXMUkPtsigDzlHtkdf9BTiF6QMs3gqhHPLWgsPAzB
 gpjQc//X2e/77saaTdrwj10vy587xxRLYWF92aOVq9s7CKb5g/D3q5DuaeRpVOum
 w2WugVGxPs0Fsy2Wd0HuZFpeU6qBD9AdHfB0GMJ4TQs8K2XT9vLAc9cw6x4s3Bup
 PwbqfN+upLZHbiL0uZpXfq39w1JUr/QkH7jCMpu2xZvc0R472Nv6FGLDdyLAYJnG
 HNzLXWW9wGj4YjTPKYQD
 =YrWd
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-monitor-2015-11-26' into staging

QMP and QObject patches

# gpg: Signature made Thu 26 Nov 2015 09:07:18 GMT using RSA key ID EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>"

* remotes/armbru/tags/pull-monitor-2015-11-26:
  qjson: Limit number of tokens in addition to total size
  qjson: surprise, allocating 6 QObjects per token is expensive
  qjson: store tokens in a GQueue
  qjson: Convert to parser to recursive descent
  qjson: replace QString in JSONLexer with GString
  qjson: Inline token_is_escape() and simplify
  qjson: Inline token_is_keyword() and simplify
  qjson: Give each of the six structural chars its own token type
  qjson: Spell out some silent assumptions
  check-qjson: Add test for JSON nesting depth limit
  qjson: Don't crash when input exceeds nesting limit
  qjson: Apply nesting limit more sanely
  monitor: Plug memory leak on QMP error

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
stable-2.5
Peter Maydell 2015-11-26 16:27:26 +00:00
commit a5df35070a
11 changed files with 225 additions and 314 deletions

View File

@ -14,11 +14,16 @@
#ifndef QEMU_JSON_LEXER_H
#define QEMU_JSON_LEXER_H
#include "qapi/qmp/qstring.h"
#include "qapi/qmp/qlist.h"
#include "glib-compat.h"
typedef enum json_token_type {
JSON_OPERATOR = 100,
JSON_MIN = 100,
JSON_LCURLY = JSON_MIN,
JSON_RCURLY,
JSON_LSQUARE,
JSON_RSQUARE,
JSON_COLON,
JSON_COMMA,
JSON_INTEGER,
JSON_FLOAT,
JSON_KEYWORD,
@ -30,13 +35,14 @@ typedef enum json_token_type {
typedef struct JSONLexer JSONLexer;
typedef void (JSONLexerEmitter)(JSONLexer *, QString *, JSONTokenType, int x, int y);
typedef void (JSONLexerEmitter)(JSONLexer *, GString *,
JSONTokenType, int x, int y);
struct JSONLexer
{
JSONLexerEmitter *emit;
int state;
QString *token;
GString *token;
int x, y;
};

View File

@ -18,7 +18,7 @@
#include "qapi/qmp/qlist.h"
#include "qapi/error.h"
QObject *json_parser_parse(QList *tokens, va_list *ap);
QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp);
QObject *json_parser_parse(GQueue *tokens, va_list *ap);
QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp);
#endif

View File

@ -14,21 +14,29 @@
#ifndef QEMU_JSON_STREAMER_H
#define QEMU_JSON_STREAMER_H
#include "qapi/qmp/qlist.h"
#include <stdint.h>
#include "glib-compat.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, QList *tokens);
void (*emit)(struct JSONMessageParser *parser, GQueue *tokens);
JSONLexer lexer;
int brace_count;
int bracket_count;
QList *tokens;
GQueue *tokens;
uint64_t token_size;
} JSONMessageParser;
void json_message_parser_init(JSONMessageParser *parser,
void (*func)(JSONMessageParser *, QList *));
void (*func)(JSONMessageParser *, GQueue *));
int json_message_parser_feed(JSONMessageParser *parser,
const char *buffer, size_t size);

View File

@ -3849,7 +3849,7 @@ static QDict *qmp_check_input_obj(QObject *input_obj, Error **errp)
return input_dict;
}
static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
{
Error *local_err = NULL;
QObject *obj, *data;
@ -3907,6 +3907,7 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
err_out:
monitor_protocol_emitter(mon, data, local_err);
qobject_decref(data);
error_free(local_err);
QDECREF(input);
QDECREF(args);
}

View File

@ -570,7 +570,7 @@ static void process_command(GAState *s, QDict *req)
}
/* handle requests/control events coming in over the channel */
static void process_event(JSONMessageParser *parser, QList *tokens)
static void process_event(JSONMessageParser *parser, GQueue *tokens)
{
GAState *s = container_of(parser, GAState, parser);
QDict *qdict;

View File

@ -11,12 +11,9 @@
*
*/
#include "qapi/qmp/qstring.h"
#include "qapi/qmp/qlist.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qint.h"
#include "qemu-common.h"
#include "qapi/qmp/json-lexer.h"
#include <stdint.h>
#define MAX_TOKEN_SIZE (64ULL << 20)
@ -30,7 +27,7 @@
*/
enum json_lexer_state {
IN_ERROR = 0,
IN_ERROR = 0, /* must really be 0, see json_lexer[] */
IN_DQ_UCODE3,
IN_DQ_UCODE2,
IN_DQ_UCODE1,
@ -62,6 +59,8 @@ enum json_lexer_state {
IN_START,
};
QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START);
#define TERMINAL(state) [0 ... 0x7F] = (state)
/* Return whether TERMINAL is a terminal state and the transition to it
@ -71,6 +70,8 @@ enum json_lexer_state {
(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,
@ -253,12 +254,12 @@ static const uint8_t json_lexer[][256] = {
['0'] = IN_ZERO,
['1' ... '9'] = IN_NONZERO_NUMBER,
['-'] = IN_NEG_NONZERO_NUMBER,
['{'] = JSON_OPERATOR,
['}'] = JSON_OPERATOR,
['['] = JSON_OPERATOR,
[']'] = JSON_OPERATOR,
[','] = JSON_OPERATOR,
[':'] = JSON_OPERATOR,
['{'] = JSON_LCURLY,
['}'] = JSON_RCURLY,
['['] = JSON_LSQUARE,
[']'] = JSON_RSQUARE,
[','] = JSON_COMMA,
[':'] = JSON_COLON,
['a' ... 'z'] = IN_KEYWORD,
['%'] = IN_ESCAPE,
[' '] = IN_WHITESPACE,
@ -272,7 +273,7 @@ void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func)
{
lexer->emit = func;
lexer->state = IN_START;
lexer->token = qstring_new();
lexer->token = g_string_sized_new(3);
lexer->x = lexer->y = 0;
}
@ -287,14 +288,20 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
}
do {
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) {
qstring_append_chr(lexer->token, ch);
g_string_append_c(lexer->token, ch);
}
switch (new_state) {
case JSON_OPERATOR:
case JSON_LCURLY:
case JSON_RCURLY:
case JSON_LSQUARE:
case JSON_RSQUARE:
case JSON_COLON:
case JSON_COMMA:
case JSON_ESCAPE:
case JSON_INTEGER:
case JSON_FLOAT:
@ -303,8 +310,7 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
lexer->emit(lexer, lexer->token, new_state, lexer->x, lexer->y);
/* fall through */
case JSON_SKIP:
QDECREF(lexer->token);
lexer->token = qstring_new();
g_string_truncate(lexer->token, 0);
new_state = IN_START;
break;
case IN_ERROR:
@ -322,8 +328,7 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
* induce an error/flush state.
*/
lexer->emit(lexer, lexer->token, JSON_ERROR, lexer->x, lexer->y);
QDECREF(lexer->token);
lexer->token = qstring_new();
g_string_truncate(lexer->token, 0);
new_state = IN_START;
lexer->state = new_state;
return 0;
@ -336,10 +341,9 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
/* Do not let a single token grow to an arbitrarily large size,
* this is a security consideration.
*/
if (lexer->token->length > MAX_TOKEN_SIZE) {
if (lexer->token->len > MAX_TOKEN_SIZE) {
lexer->emit(lexer, lexer->token, lexer->state, lexer->x, lexer->y);
QDECREF(lexer->token);
lexer->token = qstring_new();
g_string_truncate(lexer->token, 0);
lexer->state = IN_START;
}
@ -369,5 +373,5 @@ int json_lexer_flush(JSONLexer *lexer)
void json_lexer_destroy(JSONLexer *lexer)
{
QDECREF(lexer->token);
g_string_free(lexer->token, true);
}

View File

@ -22,15 +22,13 @@
#include "qapi/qmp/qbool.h"
#include "qapi/qmp/json-parser.h"
#include "qapi/qmp/json-lexer.h"
#include "qapi/qmp/json-streamer.h"
typedef struct JSONParserContext
{
Error *err;
struct {
QObject **buf;
size_t pos;
size_t count;
} tokens;
JSONToken *current;
GQueue *buf;
} JSONParserContext;
#define BUG_ON(cond) assert(!(cond))
@ -46,59 +44,11 @@ typedef struct JSONParserContext
static QObject *parse_value(JSONParserContext *ctxt, va_list *ap);
/**
* Token manipulators
*
* tokens are dictionaries that contain a type, a string value, and geometry information
* about a token identified by the lexer. These are routines that make working with
* these objects a bit easier.
*/
static const char *token_get_value(QObject *obj)
{
return qdict_get_str(qobject_to_qdict(obj), "token");
}
static JSONTokenType token_get_type(QObject *obj)
{
return qdict_get_int(qobject_to_qdict(obj), "type");
}
static int token_is_operator(QObject *obj, char op)
{
const char *val;
if (token_get_type(obj) != JSON_OPERATOR) {
return 0;
}
val = token_get_value(obj);
return (val[0] == op) && (val[1] == 0);
}
static int token_is_keyword(QObject *obj, const char *value)
{
if (token_get_type(obj) != JSON_KEYWORD) {
return 0;
}
return strcmp(token_get_value(obj), value) == 0;
}
static int token_is_escape(QObject *obj, const char *value)
{
if (token_get_type(obj) != JSON_ESCAPE) {
return 0;
}
return (strcmp(token_get_value(obj), value) == 0);
}
/**
* Error handler
*/
static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt,
QObject *token, const char *msg, ...)
JSONToken *token, const char *msg, ...)
{
va_list ap;
char message[1024];
@ -176,9 +126,10 @@ static int hex2decimal(char ch)
* \t
* \u four-hex-digits
*/
static QString *qstring_from_escaped_str(JSONParserContext *ctxt, QObject *token)
static QString *qstring_from_escaped_str(JSONParserContext *ctxt,
JSONToken *token)
{
const char *ptr = token_get_value(token);
const char *ptr = token->str;
QString *str;
int double_quote = 1;
@ -274,73 +225,34 @@ out:
return NULL;
}
static QObject *parser_context_pop_token(JSONParserContext *ctxt)
{
QObject *token;
g_assert(ctxt->tokens.pos < ctxt->tokens.count);
token = ctxt->tokens.buf[ctxt->tokens.pos];
ctxt->tokens.pos++;
return token;
}
/* Note: parser_context_{peek|pop}_token do not increment the
* token object's refcount. In both cases the references will continue
* to be tracked and cleaned up in parser_context_free(), so do not
* attempt to free the token object.
/* Note: the token object returned by parser_context_peek_token or
* parser_context_pop_token is deleted as soon as parser_context_pop_token
* is called again.
*/
static QObject *parser_context_peek_token(JSONParserContext *ctxt)
static JSONToken *parser_context_pop_token(JSONParserContext *ctxt)
{
QObject *token;
g_assert(ctxt->tokens.pos < ctxt->tokens.count);
token = ctxt->tokens.buf[ctxt->tokens.pos];
return token;
g_free(ctxt->current);
assert(!g_queue_is_empty(ctxt->buf));
ctxt->current = g_queue_pop_head(ctxt->buf);
return ctxt->current;
}
static JSONParserContext parser_context_save(JSONParserContext *ctxt)
static JSONToken *parser_context_peek_token(JSONParserContext *ctxt)
{
JSONParserContext saved_ctxt = {0};
saved_ctxt.tokens.pos = ctxt->tokens.pos;
saved_ctxt.tokens.count = ctxt->tokens.count;
saved_ctxt.tokens.buf = ctxt->tokens.buf;
return saved_ctxt;
assert(!g_queue_is_empty(ctxt->buf));
return g_queue_peek_head(ctxt->buf);
}
static void parser_context_restore(JSONParserContext *ctxt,
JSONParserContext saved_ctxt)
{
ctxt->tokens.pos = saved_ctxt.tokens.pos;
ctxt->tokens.count = saved_ctxt.tokens.count;
ctxt->tokens.buf = saved_ctxt.tokens.buf;
}
static void tokens_append_from_iter(QObject *obj, void *opaque)
{
JSONParserContext *ctxt = opaque;
g_assert(ctxt->tokens.pos < ctxt->tokens.count);
ctxt->tokens.buf[ctxt->tokens.pos++] = obj;
qobject_incref(obj);
}
static JSONParserContext *parser_context_new(QList *tokens)
static JSONParserContext *parser_context_new(GQueue *tokens)
{
JSONParserContext *ctxt;
size_t count;
if (!tokens) {
return NULL;
}
count = qlist_size(tokens);
if (count == 0) {
return NULL;
}
ctxt = g_malloc0(sizeof(JSONParserContext));
ctxt->tokens.pos = 0;
ctxt->tokens.count = count;
ctxt->tokens.buf = g_malloc(count * sizeof(QObject *));
qlist_iter(tokens, tokens_append_from_iter, ctxt);
ctxt->tokens.pos = 0;
ctxt->buf = tokens;
return ctxt;
}
@ -348,12 +260,12 @@ static JSONParserContext *parser_context_new(QList *tokens)
/* to support error propagation, ctxt->err must be freed separately */
static void parser_context_free(JSONParserContext *ctxt)
{
int i;
if (ctxt) {
for (i = 0; i < ctxt->tokens.count; i++) {
qobject_decref(ctxt->tokens.buf[i]);
while (!g_queue_is_empty(ctxt->buf)) {
parser_context_pop_token(ctxt);
}
g_free(ctxt->tokens.buf);
g_free(ctxt->current);
g_queue_free(ctxt->buf);
g_free(ctxt);
}
}
@ -363,8 +275,8 @@ static void parser_context_free(JSONParserContext *ctxt)
*/
static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
{
QObject *key = NULL, *token = NULL, *value, *peek;
JSONParserContext saved_ctxt = parser_context_save(ctxt);
QObject *key = NULL, *value;
JSONToken *peek, *token;
peek = parser_context_peek_token(ctxt);
if (peek == NULL) {
@ -384,7 +296,7 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
goto out;
}
if (!token_is_operator(token, ':')) {
if (token->type != JSON_COLON) {
parse_error(ctxt, token, "missing : in object pair");
goto out;
}
@ -402,7 +314,6 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
return 0;
out:
parser_context_restore(ctxt, saved_ctxt);
qobject_decref(key);
return -1;
@ -411,17 +322,10 @@ out:
static QObject *parse_object(JSONParserContext *ctxt, va_list *ap)
{
QDict *dict = NULL;
QObject *token, *peek;
JSONParserContext saved_ctxt = parser_context_save(ctxt);
JSONToken *token, *peek;
token = parser_context_pop_token(ctxt);
if (token == NULL) {
goto out;
}
if (!token_is_operator(token, '{')) {
goto out;
}
assert(token && token->type == JSON_LCURLY);
dict = qdict_new();
@ -431,7 +335,7 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap)
goto out;
}
if (!token_is_operator(peek, '}')) {
if (peek->type != JSON_RCURLY) {
if (parse_pair(ctxt, dict, ap) == -1) {
goto out;
}
@ -442,8 +346,8 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap)
goto out;
}
while (!token_is_operator(token, '}')) {
if (!token_is_operator(token, ',')) {
while (token->type != JSON_RCURLY) {
if (token->type != JSON_COMMA) {
parse_error(ctxt, token, "expected separator in dict");
goto out;
}
@ -465,7 +369,6 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap)
return QOBJECT(dict);
out:
parser_context_restore(ctxt, saved_ctxt);
QDECREF(dict);
return NULL;
}
@ -473,17 +376,10 @@ out:
static QObject *parse_array(JSONParserContext *ctxt, va_list *ap)
{
QList *list = NULL;
QObject *token, *peek;
JSONParserContext saved_ctxt = parser_context_save(ctxt);
JSONToken *token, *peek;
token = parser_context_pop_token(ctxt);
if (token == NULL) {
goto out;
}
if (!token_is_operator(token, '[')) {
goto out;
}
assert(token && token->type == JSON_LSQUARE);
list = qlist_new();
@ -493,7 +389,7 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap)
goto out;
}
if (!token_is_operator(peek, ']')) {
if (peek->type != JSON_RSQUARE) {
QObject *obj;
obj = parse_value(ctxt, ap);
@ -510,8 +406,8 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap)
goto out;
}
while (!token_is_operator(token, ']')) {
if (!token_is_operator(token, ',')) {
while (token->type != JSON_RSQUARE) {
if (token->type != JSON_COMMA) {
parse_error(ctxt, token, "expected separator in list");
goto out;
}
@ -537,99 +433,68 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap)
return QOBJECT(list);
out:
parser_context_restore(ctxt, saved_ctxt);
QDECREF(list);
return NULL;
}
static QObject *parse_keyword(JSONParserContext *ctxt)
{
QObject *token, *ret;
JSONParserContext saved_ctxt = parser_context_save(ctxt);
JSONToken *token;
token = parser_context_pop_token(ctxt);
if (token == NULL) {
goto out;
assert(token && token->type == JSON_KEYWORD);
if (!strcmp(token->str, "true")) {
return QOBJECT(qbool_from_bool(true));
} else if (!strcmp(token->str, "false")) {
return QOBJECT(qbool_from_bool(false));
} else if (!strcmp(token->str, "null")) {
return qnull();
}
if (token_get_type(token) != JSON_KEYWORD) {
goto out;
}
if (token_is_keyword(token, "true")) {
ret = QOBJECT(qbool_from_bool(true));
} else if (token_is_keyword(token, "false")) {
ret = QOBJECT(qbool_from_bool(false));
} else if (token_is_keyword(token, "null")) {
ret = qnull();
} else {
parse_error(ctxt, token, "invalid keyword `%s'", token_get_value(token));
goto out;
}
return ret;
out:
parser_context_restore(ctxt, saved_ctxt);
parse_error(ctxt, token, "invalid keyword '%s'", token->str);
return NULL;
}
static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap)
{
QObject *token = NULL, *obj;
JSONParserContext saved_ctxt = parser_context_save(ctxt);
JSONToken *token;
if (ap == NULL) {
goto out;
return NULL;
}
token = parser_context_pop_token(ctxt);
if (token == NULL) {
goto out;
assert(token && token->type == JSON_ESCAPE);
if (!strcmp(token->str, "%p")) {
return va_arg(*ap, QObject *);
} else if (!strcmp(token->str, "%i")) {
return QOBJECT(qbool_from_bool(va_arg(*ap, int)));
} else if (!strcmp(token->str, "%d")) {
return QOBJECT(qint_from_int(va_arg(*ap, int)));
} else if (!strcmp(token->str, "%ld")) {
return QOBJECT(qint_from_int(va_arg(*ap, long)));
} else if (!strcmp(token->str, "%lld") ||
!strcmp(token->str, "%I64d")) {
return QOBJECT(qint_from_int(va_arg(*ap, long long)));
} else if (!strcmp(token->str, "%s")) {
return QOBJECT(qstring_from_str(va_arg(*ap, const char *)));
} else if (!strcmp(token->str, "%f")) {
return QOBJECT(qfloat_from_double(va_arg(*ap, double)));
}
if (token_is_escape(token, "%p")) {
obj = va_arg(*ap, QObject *);
} else if (token_is_escape(token, "%i")) {
obj = QOBJECT(qbool_from_bool(va_arg(*ap, int)));
} else if (token_is_escape(token, "%d")) {
obj = QOBJECT(qint_from_int(va_arg(*ap, int)));
} else if (token_is_escape(token, "%ld")) {
obj = QOBJECT(qint_from_int(va_arg(*ap, long)));
} else if (token_is_escape(token, "%lld") ||
token_is_escape(token, "%I64d")) {
obj = QOBJECT(qint_from_int(va_arg(*ap, long long)));
} else if (token_is_escape(token, "%s")) {
obj = QOBJECT(qstring_from_str(va_arg(*ap, const char *)));
} else if (token_is_escape(token, "%f")) {
obj = QOBJECT(qfloat_from_double(va_arg(*ap, double)));
} else {
goto out;
}
return obj;
out:
parser_context_restore(ctxt, saved_ctxt);
return NULL;
}
static QObject *parse_literal(JSONParserContext *ctxt)
{
QObject *token, *obj;
JSONParserContext saved_ctxt = parser_context_save(ctxt);
JSONToken *token;
token = parser_context_pop_token(ctxt);
if (token == NULL) {
goto out;
}
assert(token);
switch (token_get_type(token)) {
switch (token->type) {
case JSON_STRING:
obj = QOBJECT(qstring_from_escaped_str(ctxt, token));
break;
return QOBJECT(qstring_from_escaped_str(ctxt, token));
case JSON_INTEGER: {
/* A possibility exists that this is a whole-valued float where the
* fractional part was left out due to being 0 (.0). It's not a big
@ -646,56 +511,55 @@ static QObject *parse_literal(JSONParserContext *ctxt)
int64_t value;
errno = 0; /* strtoll doesn't set errno on success */
value = strtoll(token_get_value(token), NULL, 10);
value = strtoll(token->str, NULL, 10);
if (errno != ERANGE) {
obj = QOBJECT(qint_from_int(value));
break;
return QOBJECT(qint_from_int(value));
}
/* fall through to JSON_FLOAT */
}
case JSON_FLOAT:
/* FIXME dependent on locale */
obj = QOBJECT(qfloat_from_double(strtod(token_get_value(token), NULL)));
break;
return QOBJECT(qfloat_from_double(strtod(token->str, NULL)));
default:
goto out;
abort();
}
return obj;
out:
parser_context_restore(ctxt, saved_ctxt);
return NULL;
}
static QObject *parse_value(JSONParserContext *ctxt, va_list *ap)
{
QObject *obj;
JSONToken *token;
obj = parse_object(ctxt, ap);
if (obj == NULL) {
obj = parse_array(ctxt, ap);
}
if (obj == NULL) {
obj = parse_escape(ctxt, ap);
}
if (obj == NULL) {
obj = parse_keyword(ctxt);
}
if (obj == NULL) {
obj = parse_literal(ctxt);
token = parser_context_peek_token(ctxt);
if (token == NULL) {
parse_error(ctxt, NULL, "premature EOI");
return NULL;
}
return obj;
switch (token->type) {
case JSON_LCURLY:
return parse_object(ctxt, ap);
case JSON_LSQUARE:
return parse_array(ctxt, ap);
case JSON_ESCAPE:
return parse_escape(ctxt, ap);
case JSON_INTEGER:
case JSON_FLOAT:
case JSON_STRING:
return parse_literal(ctxt);
case JSON_KEYWORD:
return parse_keyword(ctxt);
default:
parse_error(ctxt, token, "expecting value");
return NULL;
}
}
QObject *json_parser_parse(QList *tokens, 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(QList *tokens, va_list *ap, Error **errp)
QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp)
{
JSONParserContext *ctxt = parser_context_new(tokens);
QObject *result;

View File

@ -11,50 +11,55 @@
*
*/
#include "qapi/qmp/qlist.h"
#include "qapi/qmp/qint.h"
#include "qapi/qmp/qdict.h"
#include "qemu-common.h"
#include "qapi/qmp/json-lexer.h"
#include "qapi/qmp/json-streamer.h"
#define MAX_TOKEN_SIZE (64ULL << 20)
#define MAX_TOKEN_COUNT (2ULL << 20)
#define MAX_NESTING (1ULL << 10)
static void json_message_process_token(JSONLexer *lexer, QString *token, JSONTokenType type, int x, int y)
static void json_message_free_tokens(JSONMessageParser *parser)
{
if (parser->tokens) {
g_queue_free(parser->tokens);
parser->tokens = NULL;
}
}
static void json_message_process_token(JSONLexer *lexer, GString *input,
JSONTokenType type, int x, int y)
{
JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer);
QDict *dict;
JSONToken *token;
if (type == JSON_OPERATOR) {
switch (qstring_get_str(token)[0]) {
case '{':
parser->brace_count++;
break;
case '}':
parser->brace_count--;
break;
case '[':
parser->bracket_count++;
break;
case ']':
parser->bracket_count--;
break;
default:
break;
}
switch (type) {
case JSON_LCURLY:
parser->brace_count++;
break;
case JSON_RCURLY:
parser->brace_count--;
break;
case JSON_LSQUARE:
parser->bracket_count++;
break;
case JSON_RSQUARE:
parser->bracket_count--;
break;
default:
break;
}
dict = qdict_new();
qdict_put(dict, "type", qint_from_int(type));
QINCREF(token);
qdict_put(dict, "token", token);
qdict_put(dict, "x", qint_from_int(x));
qdict_put(dict, "y", qint_from_int(y));
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 += token->length;
parser->token_size += input->len;
qlist_append(parser->tokens, dict);
g_queue_push_tail(parser->tokens, token);
if (type == JSON_ERROR) {
goto out_emit_bad;
@ -64,41 +69,39 @@ static void json_message_process_token(JSONLexer *lexer, QString *token, JSONTok
parser->bracket_count == 0)) {
goto out_emit;
} else if (parser->token_size > MAX_TOKEN_SIZE ||
parser->bracket_count > MAX_NESTING ||
parser->brace_count > MAX_NESTING) {
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;
goto out_emit_bad;
}
return;
out_emit_bad:
/* clear out token list and tell the parser to emit and error
/*
* Clear out token list and tell the parser to emit an error
* indication by passing it a NULL list
*/
QDECREF(parser->tokens);
parser->tokens = NULL;
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. */
parser->emit(parser, parser->tokens);
if (parser->tokens) {
QDECREF(parser->tokens);
}
parser->tokens = qlist_new();
parser->tokens = g_queue_new();
parser->token_size = 0;
}
void json_message_parser_init(JSONMessageParser *parser,
void (*func)(JSONMessageParser *, QList *))
void (*func)(JSONMessageParser *, GQueue *))
{
parser->emit = func;
parser->brace_count = 0;
parser->bracket_count = 0;
parser->tokens = qlist_new();
parser->tokens = g_queue_new();
parser->token_size = 0;
json_lexer_init(&parser->lexer, json_message_process_token);
@ -118,5 +121,5 @@ int json_message_parser_flush(JSONMessageParser *parser)
void json_message_parser_destroy(JSONMessageParser *parser)
{
json_lexer_destroy(&parser->lexer);
QDECREF(parser->tokens);
json_message_free_tokens(parser);
}

View File

@ -28,7 +28,7 @@ typedef struct JSONParsingState
QObject *result;
} JSONParsingState;
static void parse_json(JSONMessageParser *parser, QList *tokens)
static void parse_json(JSONMessageParser *parser, GQueue *tokens)
{
JSONParsingState *s = container_of(parser, JSONParsingState, parser);
s->result = json_parser_parse(tokens, s->ap);

View File

@ -1484,6 +1484,30 @@ static void unterminated_literal(void)
g_assert(obj == NULL);
}
static char *make_nest(char *buf, size_t cnt)
{
memset(buf, '[', cnt - 1);
buf[cnt - 1] = '{';
buf[cnt] = '}';
memset(buf + cnt + 1, ']', cnt - 1);
buf[2 * cnt] = 0;
return buf;
}
static void limits_nesting(void)
{
enum { max_nesting = 1024 }; /* see qobject/json-streamer.c */
char buf[2 * (max_nesting + 1) + 1];
QObject *obj;
obj = qobject_from_json(make_nest(buf, max_nesting));
g_assert(obj != NULL);
qobject_decref(obj);
obj = qobject_from_json(make_nest(buf, max_nesting + 1));
g_assert(obj == NULL);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@ -1519,6 +1543,7 @@ int main(int argc, char **argv)
g_test_add_func("/errors/invalid_array_comma", invalid_array_comma);
g_test_add_func("/errors/invalid_dict_comma", invalid_dict_comma);
g_test_add_func("/errors/unterminated/literal", unterminated_literal);
g_test_add_func("/errors/limits/nesting", limits_nesting);
return g_test_run();
}

View File

@ -351,7 +351,7 @@ typedef struct {
QDict *response;
} QMPResponseParser;
static void qmp_response(JSONMessageParser *parser, QList *tokens)
static void qmp_response(JSONMessageParser *parser, GQueue *tokens)
{
QMPResponseParser *qmp = container_of(parser, QMPResponseParser, parser);
QObject *obj;