From 66008564f8ce570f7ad6368fbde2138e946d328b Mon Sep 17 00:00:00 2001 From: Noah Misch Date: Wed, 12 Jun 2013 19:51:12 -0400 Subject: [PATCH] Avoid reading past datum end when parsing JSON. Several loops in the JSON parser examined a byte in memory just before checking whether its address was in-bounds, so they could read one byte beyond the datum's allocation. A SIGSEGV is possible. New in 9.3, so no back-patch. --- src/backend/utils/adt/json.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index a1c7f51efa..a231736345 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -598,7 +598,7 @@ json_lex(JsonLexContext *lex) * the whole word as an unexpected token, rather than just * some unintuitive prefix thereof. */ - for (p = s; JSON_ALPHANUMERIC_CHAR(*p) && p - s < lex->input_length - len; p++) + for (p = s; p - s < lex->input_length - len && JSON_ALPHANUMERIC_CHAR(*p); p++) /* skip */ ; /* @@ -651,16 +651,21 @@ json_lex_string(JsonLexContext *lex) if (lex->strval != NULL) resetStringInfo(lex->strval); + Assert(lex->input_length > 0); + s = lex->token_start; len = lex->token_start - lex->input; - len++; - for (s = lex->token_start + 1; *s != '"'; s++, len++) + for (;;) { + s++; + len++; /* Premature end of the string. */ if (len >= lex->input_length) { lex->token_terminator = s; report_invalid_token(lex); } + else if (*s == '"') + break; else if ((unsigned char) *s < 32) { /* Per RFC4627, these characters MUST be escaped. */ @@ -921,7 +926,7 @@ json_lex_number(JsonLexContext *lex, char *s) { s++; len++; - } while (*s >= '0' && *s <= '9' && len < lex->input_length); + } while (len < lex->input_length && *s >= '0' && *s <= '9'); } else error = true; @@ -939,7 +944,7 @@ json_lex_number(JsonLexContext *lex, char *s) { s++; len++; - } while (*s >= '0' && *s <= '9' && len < lex->input_length); + } while (len < lex->input_length && *s >= '0' && *s <= '9'); } } @@ -970,7 +975,7 @@ json_lex_number(JsonLexContext *lex, char *s) * here should be considered part of the token for error-reporting * purposes. */ - for (p = s; JSON_ALPHANUMERIC_CHAR(*p) && len < lex->input_length; p++, len++) + for (p = s; len < lex->input_length && JSON_ALPHANUMERIC_CHAR(*p); p++, len++) error = true; lex->prev_token_terminator = lex->token_terminator; lex->token_terminator = p; @@ -1138,8 +1143,8 @@ report_json_context(JsonLexContext *lex) line_number = 1; for (;;) { - /* Always advance over newlines (context_end test is just paranoia) */ - if (*context_start == '\n' && context_start < context_end) + /* Always advance over newlines */ + if (context_start < context_end && *context_start == '\n') { context_start++; line_start = context_start;