Avoid integer overflow in hstore_to_json().

The length of the output buffer was calculated based on the size of the
argument hstore. On a sizeof(int) == 4 platform and a huge argument, it
could overflow, causing a too small buffer to be allocated.

Refactor the function to use a StringInfo instead of pre-allocating the
buffer. Makes it shorter and more readable, too.
This commit is contained in:
Heikki Linnakangas 2014-02-21 15:43:31 +02:00
parent 8c059dffd8
commit 0c5783ff30
1 changed files with 41 additions and 109 deletions

View File

@ -1245,77 +1245,49 @@ Datum
hstore_to_json_loose(PG_FUNCTION_ARGS) hstore_to_json_loose(PG_FUNCTION_ARGS)
{ {
HStore *in = PG_GETARG_HS(0); HStore *in = PG_GETARG_HS(0);
int buflen, int i;
i;
int count = HS_COUNT(in); int count = HS_COUNT(in);
char *out,
*ptr;
char *base = STRPTR(in); char *base = STRPTR(in);
HEntry *entries = ARRPTR(in); HEntry *entries = ARRPTR(in);
bool is_number; bool is_number;
StringInfo src, StringInfoData tmp,
dst; dst;
if (count == 0) if (count == 0)
PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2)); PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2));
buflen = 3; initStringInfo(&tmp);
initStringInfo(&dst);
/* appendStringInfoChar(&dst, '{');
* Formula adjusted slightly from the logic in hstore_out. We have to take
* account of out treatment of booleans to be a bit more pessimistic about
* the length of values.
*/
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
/* include "" and colon-space and comma-space */ resetStringInfo(&tmp);
buflen += 6 + 2 * HS_KEYLEN(entries, i); appendBinaryStringInfo(&tmp, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
/* include "" only if nonnull */ escape_json(&dst, tmp.data);
buflen += 3 + (HS_VALISNULL(entries, i) appendStringInfoString(&dst, ": ");
? 1
: 2 * HS_VALLEN(entries, i));
}
out = ptr = palloc(buflen);
src = makeStringInfo();
dst = makeStringInfo();
*ptr++ = '{';
for (i = 0; i < count; i++)
{
resetStringInfo(src);
resetStringInfo(dst);
appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
escape_json(dst, src->data);
strncpy(ptr, dst->data, dst->len);
ptr += dst->len;
*ptr++ = ':';
*ptr++ = ' ';
resetStringInfo(dst);
if (HS_VALISNULL(entries, i)) if (HS_VALISNULL(entries, i))
appendStringInfoString(dst, "null"); appendStringInfoString(&dst, "null");
/* guess that values of 't' or 'f' are booleans */ /* guess that values of 't' or 'f' are booleans */
else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 't') else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 't')
appendStringInfoString(dst, "true"); appendStringInfoString(&dst, "true");
else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 'f') else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 'f')
appendStringInfoString(dst, "false"); appendStringInfoString(&dst, "false");
else else
{ {
is_number = false; is_number = false;
resetStringInfo(src); resetStringInfo(&tmp);
appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
/* /*
* don't treat something with a leading zero followed by another * don't treat something with a leading zero followed by another
* digit as numeric - could be a zip code or similar * digit as numeric - could be a zip code or similar
*/ */
if (src->len > 0 && if (tmp.len > 0 &&
!(src->data[0] == '0' && !(tmp.data[0] == '0' &&
isdigit((unsigned char) src->data[1])) && isdigit((unsigned char) tmp.data[1])) &&
strspn(src->data, "+-0123456789Ee.") == src->len) strspn(tmp.data, "+-0123456789Ee.") == tmp.len)
{ {
/* /*
* might be a number. See if we can input it as a numeric * might be a number. See if we can input it as a numeric
@ -1324,7 +1296,7 @@ hstore_to_json_loose(PG_FUNCTION_ARGS)
char *endptr = "junk"; char *endptr = "junk";
long lval; long lval;
lval = strtol(src->data, &endptr, 10); lval = strtol(tmp.data, &endptr, 10);
(void) lval; (void) lval;
if (*endptr == '\0') if (*endptr == '\0')
{ {
@ -1339,30 +1311,24 @@ hstore_to_json_loose(PG_FUNCTION_ARGS)
/* not an int - try a double */ /* not an int - try a double */
double dval; double dval;
dval = strtod(src->data, &endptr); dval = strtod(tmp.data, &endptr);
(void) dval; (void) dval;
if (*endptr == '\0') if (*endptr == '\0')
is_number = true; is_number = true;
} }
} }
if (is_number) if (is_number)
appendBinaryStringInfo(dst, src->data, src->len); appendBinaryStringInfo(&dst, tmp.data, tmp.len);
else else
escape_json(dst, src->data); escape_json(&dst, tmp.data);
} }
strncpy(ptr, dst->data, dst->len);
ptr += dst->len;
if (i + 1 != count) if (i + 1 != count)
{ appendStringInfoString(&dst, ", ");
*ptr++ = ',';
*ptr++ = ' ';
} }
} appendStringInfoChar(&dst, '}');
*ptr++ = '}';
*ptr = '\0';
PG_RETURN_TEXT_P(cstring_to_text(out)); PG_RETURN_TEXT_P(cstring_to_text(dst.data));
} }
PG_FUNCTION_INFO_V1(hstore_to_json); PG_FUNCTION_INFO_V1(hstore_to_json);
@ -1371,74 +1337,40 @@ Datum
hstore_to_json(PG_FUNCTION_ARGS) hstore_to_json(PG_FUNCTION_ARGS)
{ {
HStore *in = PG_GETARG_HS(0); HStore *in = PG_GETARG_HS(0);
int buflen, int i;
i;
int count = HS_COUNT(in); int count = HS_COUNT(in);
char *out,
*ptr;
char *base = STRPTR(in); char *base = STRPTR(in);
HEntry *entries = ARRPTR(in); HEntry *entries = ARRPTR(in);
StringInfo src, StringInfoData tmp,
dst; dst;
if (count == 0) if (count == 0)
PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2)); PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2));
buflen = 3; initStringInfo(&tmp);
initStringInfo(&dst);
/* appendStringInfoChar(&dst, '{');
* Formula adjusted slightly from the logic in hstore_out. We have to take
* account of out treatment of booleans to be a bit more pessimistic about
* the length of values.
*/
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
/* include "" and colon-space and comma-space */ resetStringInfo(&tmp);
buflen += 6 + 2 * HS_KEYLEN(entries, i); appendBinaryStringInfo(&tmp, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
/* include "" only if nonnull */ escape_json(&dst, tmp.data);
buflen += 3 + (HS_VALISNULL(entries, i) appendStringInfoString(&dst, ": ");
? 1
: 2 * HS_VALLEN(entries, i));
}
out = ptr = palloc(buflen);
src = makeStringInfo();
dst = makeStringInfo();
*ptr++ = '{';
for (i = 0; i < count; i++)
{
resetStringInfo(src);
resetStringInfo(dst);
appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
escape_json(dst, src->data);
strncpy(ptr, dst->data, dst->len);
ptr += dst->len;
*ptr++ = ':';
*ptr++ = ' ';
resetStringInfo(dst);
if (HS_VALISNULL(entries, i)) if (HS_VALISNULL(entries, i))
appendStringInfoString(dst, "null"); appendStringInfoString(&dst, "null");
else else
{ {
resetStringInfo(src); resetStringInfo(&tmp);
appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
escape_json(dst, src->data); escape_json(&dst, tmp.data);
} }
strncpy(ptr, dst->data, dst->len);
ptr += dst->len;
if (i + 1 != count) if (i + 1 != count)
{ appendStringInfoString(&dst, ", ");
*ptr++ = ',';
*ptr++ = ' ';
} }
} appendStringInfoChar(&dst, '}');
*ptr++ = '}';
*ptr = '\0';
PG_RETURN_TEXT_P(cstring_to_text(out)); PG_RETURN_TEXT_P(cstring_to_text(dst.data));
} }