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:
parent
8c059dffd8
commit
0c5783ff30
@ -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++ = ' ';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
*ptr++ = '}';
|
appendStringInfoChar(&dst, '}');
|
||||||
*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++ = ' ';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
*ptr++ = '}';
|
appendStringInfoChar(&dst, '}');
|
||||||
*ptr = '\0';
|
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(cstring_to_text(out));
|
PG_RETURN_TEXT_P(cstring_to_text(dst.data));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user