Avoid reading past datum end when parsing JSON.
authorNoah Misch
Wed, 12 Jun 2013 23:51:12 +0000 (19:51 -0400)
committerNoah Misch
Wed, 12 Jun 2013 23:51:12 +0000 (19:51 -0400)
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

index a1c7f51efaa664d3d6e2a6a3c79b34461c58a3d6..a231736345223532c40965bfb8f2f4f221551634 100644 (file)
@@ -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;