mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 00:03:57 -04:00 
			
		
		
		
	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