From b9c3b5ae71c368a280ca744134d55ba487a74975 Mon Sep 17 00:00:00 2001
From: Tender Wang <tndrwang@gmail.com>
Date: Mon, 17 Aug 2026 10:00:22 +0800
Subject: [PATCH v2] Fix integer overflow when constructing large hstore values

---
 contrib/hstore/hstore.h    | 25 +++++++++++---------
 contrib/hstore/hstore_io.c | 47 +++++++++++++++++++++++---------------
 contrib/hstore/hstore_op.c |  2 +-
 3 files changed, 44 insertions(+), 30 deletions(-)

diff --git a/contrib/hstore/hstore.h b/contrib/hstore/hstore.h
index 897af244a42..91aceb3ade2 100644
--- a/contrib/hstore/hstore.h
+++ b/contrib/hstore/hstore.h
@@ -62,15 +62,18 @@ typedef struct
 #define HS_SETCOUNT(hsp_,c_) ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION)
 
 
-/*
- * "x" comes from an existing HS_COUNT() (as discussed, <= INT_MAX/24) or a
- * Pairs array length (due to MaxAllocSize, <= INT_MAX/40).  "lenstr" is no
- * more than INT_MAX, that extreme case arising in hstore_from_arrays().
- * Therefore, this calculation is limited to about INT_MAX / 5 + INT_MAX.
- */
 #define HSHRDSIZE	(sizeof(HStore))
-#define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) )
 
+static inline Size
+hstoreCalcDataSize(Size count, Size lenstr)
+{
+	Size len;
+
+	len = mul_size(count, 2 * sizeof(HEntry));
+	len = add_size(len, HSHRDSIZE);
+	return add_size(len, lenstr);
+}
+#define CALCDATASIZE(x, lenstr) hstoreCalcDataSize((x), (lenstr))
 /* note multiple evaluations of x */
 #define ARRPTR(x)		( (HEntry*) ( (HStore*)(x) + 1 ) )
 #define STRPTR(x)		( (char*)(ARRPTR(x) + HS_COUNT((HStore*)(x)) * 2) )
@@ -128,7 +131,7 @@ typedef struct
 /* finalize a newly-constructed hstore */
 #define HS_FINALIZE(hsp_,count_,buf_,ptr_)							\
 	do {															\
-		int _buflen = (ptr_) - (buf_);								\
+		Size _buflen = (ptr_) - (buf_);								\
 		if ((count_))												\
 			ARRPTR(hsp_)[0].entry |= HENTRY_ISFIRST;				\
 		if ((count_) != HS_COUNT((hsp_)))							\
@@ -142,7 +145,7 @@ typedef struct
 /* ensure the varlena size of an existing hstore is correct */
 #define HS_FIXSIZE(hsp_,count_)											\
 	do {																\
-		int bl = (count_) ? HSE_ENDPOS(ARRPTR(hsp_)[2*(count_)-1]) : 0; \
+		Size bl = (count_) ? HSE_ENDPOS(ARRPTR(hsp_)[2*(count_)-1]) : 0;\
 		SET_VARSIZE((hsp_), CALCDATASIZE((count_),bl));					\
 	} while (0)
 
@@ -168,8 +171,8 @@ typedef struct
 	bool		needfree;		/* need to pfree the value? */
 } Pairs;
 
-extern PGDLLEXPORT int hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen);
-extern PGDLLEXPORT HStore *hstorePairs(Pairs *pairs, int32 pcount, int32 buflen);
+extern PGDLLEXPORT int hstoreUniquePairs(Pairs *a, int32 l, Size *buflen);
+extern PGDLLEXPORT HStore *hstorePairs(Pairs *pairs, int32 pcount, Size buflen);
 
 extern PGDLLEXPORT size_t hstoreCheckKeyLen(size_t len);
 extern PGDLLEXPORT size_t hstoreCheckValLen(size_t len);
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 9b72efb8674..fa58f98efa1 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -356,7 +356,7 @@ comparePairs(const void *a, const void *b)
  * and (b) who knows whether they might be needed by some caller.
  */
 int
-hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen)
+hstoreUniquePairs(Pairs *a, int32 l, Size *buflen)
 {
 	Pairs	   *ptr,
 			   *res;
@@ -365,7 +365,11 @@ hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen)
 	if (l < 2)
 	{
 		if (l == 1)
-			*buflen = a->keylen + ((a->isnull) ? 0 : a->vallen);
+		{
+			*buflen = add_size(*buflen, a->keylen);
+			if (!a->isnull)
+				*buflen = add_size(*buflen, a->vallen);
+		}
 		return l;
 	}
 
@@ -391,7 +395,9 @@ hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen)
 		}
 		else
 		{
-			*buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
+			*buflen = add_size(*buflen, res->keylen);
+			if (!res->isnull)
+				*buflen = add_size(*buflen, res->vallen);
 			res++;
 			if (res != ptr)
 				memcpy(res, ptr, sizeof(Pairs));
@@ -400,7 +406,9 @@ hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen)
 		ptr++;
 	}
 
-	*buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
+	*buflen = add_size(*buflen, res->keylen);
+	if (!res->isnull)
+		*buflen = add_size(*buflen, res->vallen);
 	return res + 1 - a;
 }
 
@@ -446,13 +454,13 @@ hstoreCheckValLength(size_t len, HSParser *state)
 
 
 HStore *
-hstorePairs(Pairs *pairs, int32 pcount, int32 buflen)
+hstorePairs(Pairs *pairs, int32 pcount, Size buflen)
 {
 	HStore	   *out;
 	HEntry	   *entry;
 	char	   *ptr;
 	char	   *buf;
-	int32		len;
+	Size		len;
 	int32		i;
 
 	len = CALCDATASIZE(pcount, buflen);
@@ -482,7 +490,7 @@ hstore_in(PG_FUNCTION_ARGS)
 	char	   *str = PG_GETARG_CSTRING(0);
 	Node	   *escontext = fcinfo->context;
 	HSParser	state;
-	int32		buflen;
+	Size		buflen;
 	HStore	   *out;
 
 	state.begin = str;
@@ -503,7 +511,7 @@ PG_FUNCTION_INFO_V1(hstore_recv);
 Datum
 hstore_recv(PG_FUNCTION_ARGS)
 {
-	int32		buflen;
+	Size		buflen;
 	HStore	   *out;
 	Pairs	   *pairs;
 	int32		i;
@@ -592,7 +600,7 @@ hstore_from_text(PG_FUNCTION_ARGS)
 		p.isnull = false;
 	}
 
-	out = hstorePairs(&p, 1, p.keylen + p.vallen);
+	out = hstorePairs(&p, 1, add_size(p.keylen, p.vallen));
 
 	PG_RETURN_POINTER(out);
 }
@@ -602,7 +610,7 @@ PG_FUNCTION_INFO_V1(hstore_from_arrays);
 Datum
 hstore_from_arrays(PG_FUNCTION_ARGS)
 {
-	int32		buflen;
+	Size	   buflen;
 	HStore	   *out;
 	Pairs	   *pairs;
 	Datum	   *key_datums;
@@ -721,7 +729,7 @@ hstore_from_array(PG_FUNCTION_ARGS)
 	ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
 	int			ndims = ARR_NDIM(in_array);
 	int			count;
-	int32		buflen;
+	Size	   buflen;
 	HStore	   *out;
 	Pairs	   *pairs;
 	Datum	   *in_datums;
@@ -835,7 +843,7 @@ Datum
 hstore_from_record(PG_FUNCTION_ARGS)
 {
 	HeapTupleHeader rec;
-	int32		buflen;
+	Size	   buflen;
 	HStore	   *out;
 	Pairs	   *pairs;
 	Oid			tupType;
@@ -1226,8 +1234,8 @@ Datum
 hstore_out(PG_FUNCTION_ARGS)
 {
 	HStore	   *in = PG_GETARG_HSTORE_P(0);
-	int			buflen,
-				i;
+	Size		buflen;
+	int			i;
 	int			count = HS_COUNT(in);
 	char	   *out,
 			   *ptr;
@@ -1250,11 +1258,14 @@ hstore_out(PG_FUNCTION_ARGS)
 	for (i = 0; i < count; i++)
 	{
 		/* include "" and => and comma-space */
-		buflen += 6 + 2 * HSTORE_KEYLEN(entries, i);
+		buflen = add_size(buflen, 6);
+		buflen = add_size(buflen, mul_size(2, HSTORE_KEYLEN(entries, i)));
 		/* include "" only if nonnull */
-		buflen += 2 + (HSTORE_VALISNULL(entries, i)
-					   ? 2
-					   : 2 * HSTORE_VALLEN(entries, i));
+		buflen = add_size(buflen, 2);
+		if (HSTORE_VALISNULL(entries, i))
+			buflen = add_size(buflen, 2);
+		else
+			buflen = add_size(buflen, mul_size(2, HSTORE_VALLEN(entries, i)));
 	}
 
 	out = ptr = palloc(buflen);
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index bcba75f9258..19f8b6b9684 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -76,7 +76,7 @@ hstoreArrayToPairs(ArrayType *a, int *npairs)
 	bool	   *key_nulls;
 	int			key_count;
 	Pairs	   *key_pairs;
-	int			bufsiz;
+	Size		bufsiz;
 	int			i,
 				j;
 
-- 
2.43.0

