Commit 66008564 authored by Noah Misch's avatar Noah Misch

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.
parent 3a5d0c55
...@@ -598,7 +598,7 @@ json_lex(JsonLexContext *lex) ...@@ -598,7 +598,7 @@ json_lex(JsonLexContext *lex)
* the whole word as an unexpected token, rather than just * the whole word as an unexpected token, rather than just
* some unintuitive prefix thereof. * 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 */ ; /* skip */ ;
/* /*
...@@ -651,16 +651,21 @@ json_lex_string(JsonLexContext *lex) ...@@ -651,16 +651,21 @@ json_lex_string(JsonLexContext *lex)
if (lex->strval != NULL) if (lex->strval != NULL)
resetStringInfo(lex->strval); resetStringInfo(lex->strval);
Assert(lex->input_length > 0);
s = lex->token_start;
len = lex->token_start - lex->input; len = lex->token_start - lex->input;
len++; for (;;)
for (s = lex->token_start + 1; *s != '"'; s++, len++)
{ {
s++;
len++;
/* Premature end of the string. */ /* Premature end of the string. */
if (len >= lex->input_length) if (len >= lex->input_length)
{ {
lex->token_terminator = s; lex->token_terminator = s;
report_invalid_token(lex); report_invalid_token(lex);
} }
else if (*s == '"')
break;
else if ((unsigned char) *s < 32) else if ((unsigned char) *s < 32)
{ {
/* Per RFC4627, these characters MUST be escaped. */ /* Per RFC4627, these characters MUST be escaped. */
...@@ -921,7 +926,7 @@ json_lex_number(JsonLexContext *lex, char *s) ...@@ -921,7 +926,7 @@ json_lex_number(JsonLexContext *lex, char *s)
{ {
s++; s++;
len++; len++;
} while (*s >= '0' && *s <= '9' && len < lex->input_length); } while (len < lex->input_length && *s >= '0' && *s <= '9');
} }
else else
error = true; error = true;
...@@ -939,7 +944,7 @@ json_lex_number(JsonLexContext *lex, char *s) ...@@ -939,7 +944,7 @@ json_lex_number(JsonLexContext *lex, char *s)
{ {
s++; s++;
len++; 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) ...@@ -970,7 +975,7 @@ json_lex_number(JsonLexContext *lex, char *s)
* here should be considered part of the token for error-reporting * here should be considered part of the token for error-reporting
* purposes. * 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; error = true;
lex->prev_token_terminator = lex->token_terminator; lex->prev_token_terminator = lex->token_terminator;
lex->token_terminator = p; lex->token_terminator = p;
...@@ -1138,8 +1143,8 @@ report_json_context(JsonLexContext *lex) ...@@ -1138,8 +1143,8 @@ report_json_context(JsonLexContext *lex)
line_number = 1; line_number = 1;
for (;;) for (;;)
{ {
/* Always advance over newlines (context_end test is just paranoia) */ /* Always advance over newlines */
if (*context_start == '\n' && context_start < context_end) if (context_start < context_end && *context_start == '\n')
{ {
context_start++; context_start++;
line_start = context_start; line_start = context_start;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment