CVE-2014-0064 (published 2014-03-31T14:58Z)

Multiple integer overflows in the path_in and other unspecified functions in PostgreSQL before 8.4.20, 9.0.x before 9.0.16, 9.1.x before 9.1.12, 9.2.x before 9.2.7, and 9.3.x before 9.3.3 allow remote authenticated users to have unspecified impact and attack vectors, which trigger a buffer overflow.  NOTE: this identifier has been SPLIT due to different affected versions; use CVE-2014-2669 for the hstore vector.

Severity: MEDIUM
CVSS v2 base score: 6.5 (AV:N/AC:L/Au:S/C:P/I:P/A:P)

Weakness information:
cwe_id: CWE-189
cwe_name: Numeric Errors
description: Weaknesses in this category are related to improper calculation or conversion of numbers.

Fix information:
repo_url: https://github.com/postgres/postgres
commit_details: repo_url: https://github.com/postgres/postgres
author: Noah Misch
author_date: 2014-02-17 09:33:31-05:00
msg: Predict integer overflow to avoid buffer overruns.

Several functions, mostly type input functions, calculated an allocation
size such that the calculation wrapped to a small positive value when
arguments implied a sufficiently-large requirement.  Writes past the end
of the inadvertent small allocation followed shortly thereafter.
Coverity identified the path_in() vulnerability; code inspection led to
the rest.  In passing, add check_stack_depth() to prevent stack overflow
in related functions.

Back-patch to 8.4 (all supported versions).  The non-comment hstore
changes touch code that did not exist in 8.4, so that part stops at 9.0.

Noah Misch and Heikki Linnakangas, reviewed by Tom Lane.

Security: CVE-2014-0064
repository: repo_url: https://github.com/postgres/postgres
repo_name: postgres/postgres
description: Mirror of the official PostgreSQL GIT repository. Note that this is just a *mirror* - we don't work with pull requests on github. To contribute, please see https://wiki.postgresql.org/wiki/Submitting_a_Patch
repo_language: C
owner: postgres
file_changes: filename: hstore.h
change_type: ModificationType.MODIFY
diff: @@ -49,9 +49,12 @@ typedef struct
 } HStore;
 
 /*
- * it's not possible to get more than 2^28 items into an hstore,
- * so we reserve the top few bits of the size field. See hstore_compat.c
- * for one reason why.	Some bits are left for future use here.
+ * It's not possible to get more than 2^28 items into an hstore, so we reserve
+ * the top few bits of the size field.  See hstore_compat.c for one reason
+ * why.  Some bits are left for future use here.  MaxAllocSize makes the
+ * practical count limit slightly more than 2^28 / 3, or INT_MAX / 24, the
+ * limit for an hstore full of 4-byte keys and null values.  Therefore, we
+ * don't explicitly check the format-imposed limit.
  */
 #define HS_FLAG_NEWVERSION 0x80000000
 
@@ -59,6 +62,12 @@ 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) )
 

programming_language: C
filename: hstore_io.c
change_type: ModificationType.MODIFY
diff: @@ -13,6 +13,7 @@
 #include "utils/builtins.h"
 #include "utils/json.h"
 #include "utils/lsyscache.h"
+#include "utils/memutils.h"
 #include "utils/typcache.h"
 
 #include "hstore.h"
@@ -439,6 +440,11 @@ hstore_recv(PG_FUNCTION_ARGS)
 		PG_RETURN_POINTER(out);
 	}
 
+	if (pcount < 0 || pcount > MaxAllocSize / sizeof(Pairs))
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+			  errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
+					 pcount, (int) (MaxAllocSize / sizeof(Pairs)))));
 	pairs = palloc(pcount * sizeof(Pairs));
 
 	for (i = 0; i < pcount; ++i)
@@ -554,6 +560,13 @@ hstore_from_arrays(PG_FUNCTION_ARGS)
 					  TEXTOID, -1, false, 'i',
 					  &key_datums, &key_nulls, &key_count);
 
+	/* see discussion in hstoreArrayToPairs() */
+	if (key_count > MaxAllocSize / sizeof(Pairs))
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+			  errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
+					 key_count, (int) (MaxAllocSize / sizeof(Pairs)))));
+
 	/* value_array might be NULL */
 
 	if (PG_ARGISNULL(1))
@@ -676,6 +689,13 @@ hstore_from_array(PG_FUNCTION_ARGS)
 
 	count = in_count / 2;
 
+	/* see discussion in hstoreArrayToPairs() */
+	if (count > MaxAllocSize / sizeof(Pairs))
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+			  errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
+					 count, (int) (MaxAllocSize / sizeof(Pairs)))));
+
 	pairs = palloc(count * sizeof(Pairs));
 
 	for (i = 0; i < count; ++i)
@@ -807,6 +827,7 @@ hstore_from_record(PG_FUNCTION_ARGS)
 		my_extra->ncolumns = ncolumns;
 	}
 
+	Assert(ncolumns <= MaxTupleAttributeNumber);		/* thus, no overflow */
 	pairs = palloc(ncolumns * sizeof(Pairs));
 
 	if (rec)

programming_language: C
filename: hstore_op.c
change_type: ModificationType.MODIFY
diff: @@ -8,6 +8,7 @@
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #include "utils/builtins.h"
+#include "utils/memutils.h"
 
 #include "hstore.h"
 
@@ -90,6 +91,19 @@ hstoreArrayToPairs(ArrayType *a, int *npairs)
 		return NULL;
 	}
 
+	/*
+	 * A text array uses at least eight bytes per element, so any overflow in
+	 * "key_count * sizeof(Pairs)" is small enough for palloc() to catch.
+	 * However, credible improvements to the array format could invalidate
+	 * that assumption.  Therefore, use an explicit check rather than relying
+	 * on palloc() to complain.
+	 */
+	if (key_count > MaxAllocSize / sizeof(Pairs))
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+			  errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
+					 key_count, (int) (MaxAllocSize / sizeof(Pairs)))));
+
 	key_pairs = palloc(sizeof(Pairs) * key_count);
 
 	for (i = 0, j = 0; i < key_count; i++)
@@ -648,6 +662,7 @@ hstore_slice_to_hstore(PG_FUNCTION_ARGS)
 		PG_RETURN_POINTER(out);
 	}
 
+	/* hstoreArrayToPairs() checked overflow */
 	out_pairs = palloc(sizeof(Pairs) * nkeys);
 	bufsiz = 0;
 

programming_language: C
filename: _int.h
change_type: ModificationType.MODIFY
diff: @@ -5,6 +5,7 @@
 #define ___INT_H__
 
 #include "utils/array.h"
+#include "utils/memutils.h"
 
 /* number ranges for compression */
 #define MAXNUMRANGE 100
@@ -137,6 +138,7 @@ typedef struct QUERYTYPE
 
 #define HDRSIZEQT	offsetof(QUERYTYPE, items)
 #define COMPUTESIZE(size)	( HDRSIZEQT + (size) * sizeof(ITEM) )
+#define QUERYTYPEMAXITEMS	((MaxAllocSize - HDRSIZEQT) / sizeof(ITEM))
 #define GETQUERY(x)  ( (x)->items )
 
 /* "type" codes for ITEM */

programming_language: C
filename: _int_bool.c
change_type: ModificationType.MODIFY
diff: @@ -448,6 +448,9 @@ boolop(PG_FUNCTION_ARGS)
 static void
 findoprnd(ITEM *ptr, int32 *pos)
 {
+	/* since this function recurses, it could be driven to stack overflow. */
+	check_stack_depth();
+
 #ifdef BS_DEBUG
 	elog(DEBUG3, (ptr[*pos].type == OPR) ?
 		 "%d  %c" : "%d  %d", *pos, ptr[*pos].val);
@@ -508,7 +511,13 @@ bqarr_in(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("empty query")));
 
+	if (state.num > QUERYTYPEMAXITEMS)
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+		errmsg("number of query items (%d) exceeds the maximum allowed (%d)",
+			   state.num, (int) QUERYTYPEMAXITEMS)));
 	commonlen = COMPUTESIZE(state.num);
+
 	query = (QUERYTYPE *) palloc(commonlen);
 	SET_VARSIZE(query, commonlen);
 	query->size = state.num;

programming_language: C
filename: ltree.h
change_type: ModificationType.MODIFY
diff: @@ -5,6 +5,7 @@
 
 #include "fmgr.h"
 #include "tsearch/ts_locale.h"
+#include "utils/memutils.h"
 
 typedef struct
 {
@@ -111,6 +112,8 @@ typedef struct
 
 #define HDRSIZEQT		MAXALIGN(VARHDRSZ + sizeof(int32))
 #define COMPUTESIZE(size,lenofoperand)	( HDRSIZEQT + (size) * sizeof(ITEM) + (lenofoperand) )
+#define LTXTQUERY_TOO_BIG(size,lenofoperand) \
+	((size) > (MaxAllocSize - HDRSIZEQT - (lenofoperand)) / sizeof(ITEM))
 #define GETQUERY(x)  (ITEM*)( (char*)(x)+HDRSIZEQT )
 #define GETOPERAND(x)	( (char*)GETQUERY(x) + ((ltxtquery*)x)->size * sizeof(ITEM) )
 

programming_language: C
filename: ltree_io.c
change_type: ModificationType.MODIFY
diff: @@ -8,6 +8,7 @@
 #include <ctype.h>
 
 #include "ltree.h"
+#include "utils/memutils.h"
 #include "crc32.h"
 
 PG_FUNCTION_INFO_V1(ltree_in);
@@ -64,6 +65,11 @@ ltree_in(PG_FUNCTION_ARGS)
 		ptr += charlen;
 	}
 
+	if (num + 1 > MaxAllocSize / sizeof(nodeitem))
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+			 errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
+					num + 1, (int) (MaxAllocSize / sizeof(nodeitem)))));
 	list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
 	ptr = buf;
 	while (*ptr)
@@ -228,6 +234,11 @@ lquery_in(PG_FUNCTION_ARGS)
 	}
 
 	num++;
+	if (num > MaxAllocSize / ITEMSIZE)
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+			 errmsg("number of levels (%d) exceeds the maximum allowed (%d)",
+					num, (int) (MaxAllocSize / ITEMSIZE))));
 	curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
 	ptr = buf;
 	while (*ptr)

programming_language: C
filename: ltxtquery_io.c
change_type: ModificationType.MODIFY
diff: @@ -9,6 +9,7 @@
 
 #include "crc32.h"
 #include "ltree.h"
+#include "miscadmin.h"
 
 PG_FUNCTION_INFO_V1(ltxtq_in);
 Datum		ltxtq_in(PG_FUNCTION_ARGS);
@@ -212,6 +213,9 @@ makepol(QPRS_STATE *state)
 	int32		lenstack = 0;
 	uint16		flag = 0;
 
+	/* since this function recurses, it could be driven to stack overflow */
+	check_stack_depth();
+
 	while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END)
 	{
 		switch (type)
@@ -276,6 +280,9 @@ makepol(QPRS_STATE *state)
 static void
 findoprnd(ITEM *ptr, int32 *pos)
 {
+	/* since this function recurses, it could be driven to stack overflow. */
+	check_stack_depth();
+
 	if (ptr[*pos].type == VAL || ptr[*pos].type == VALTRUE)
 	{
 		ptr[*pos].left = 0;
@@ -340,8 +347,12 @@ queryin(char *buf)
 				 errmsg("syntax error"),
 				 errdetail("Empty query.")));
 
-	/* make finish struct */
+	if (LTXTQUERY_TOO_BIG(state.num, state.sumlen))
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("ltxtquery is too large")));
 	commonlen = COMPUTESIZE(state.num, state.sumlen);
+
 	query = (ltxtquery *) palloc(commonlen);
 	SET_VARSIZE(query, commonlen);
 	query->size = state.num;

programming_language: C
filename: geo_ops.c
change_type: ModificationType.MODIFY
diff: @@ -1366,6 +1366,7 @@ path_in(PG_FUNCTION_ARGS)
 	char	   *s;
 	int			npts;
 	int			size;
+	int			base_size;
 	int			depth = 0;
 
 	if ((npts = pair_count(str, ',')) <= 0)
@@ -1384,7 +1385,15 @@ path_in(PG_FUNCTION_ARGS)
 		depth++;
 	}
 
-	size = offsetof(PATH, p[0]) +sizeof(path->p[0]) * npts;
+	base_size = sizeof(path->p[0]) * npts;
+	size = offsetof(PATH, p[0]) + base_size;
+
+	/* Check for integer overflow */
+	if (base_size / npts != sizeof(path->p[0]) || size <= base_size)
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("too many points requested")));
+
 	path = (PATH *) palloc(size);
 
 	SET_VARSIZE(path, size);
@@ -3429,6 +3438,7 @@ poly_in(PG_FUNCTION_ARGS)
 	POLYGON    *poly;
 	int			npts;
 	int			size;
+	int			base_size;
 	int			isopen;
 	char	   *s;
 
@@ -3437,7 +3447,15 @@ poly_in(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 			  errmsg("invalid input syntax for type polygon: \"%s\"", str)));
 
-	size = offsetof(POLYGON, p[0]) +sizeof(poly->p[0]) * npts;
+	base_size = sizeof(poly->p[0]) * npts;
+	size = offsetof(POLYGON, p[0]) + base_size;
+
+	/* Check for integer overflow */
+	if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("too many points requested")));
+
 	poly = (POLYGON *) palloc0(size);	/* zero any holes */
 
 	SET_VARSIZE(poly, size);
@@ -4343,6 +4361,10 @@ path_poly(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("open path cannot be converted to polygon")));
 
+	/*
+	 * Never overflows: the old size fit in MaxAllocSize, and the new size is
+	 * just a small constant larger.
+	 */
 	size = offsetof(POLYGON, p[0]) +sizeof(poly->p[0]) * path->npts;
 	poly = (POLYGON *) palloc(size);
 
@@ -4448,6 +4470,10 @@ poly_path(PG_FUNCTION_ARGS)
 	int			size;
 	int			i;
 
+	/*
+	 * Never overflows: the old size fit in MaxAllocSize, and the new size is
+	 * smaller by a small constant.
+	 */
 	size = offsetof(PATH, p[0]) +sizeof(path->p[0]) * poly->npts;
 	path = (PATH *) palloc(size);
 

programming_language: C
filename: tsquery.c
change_type: ModificationType.MODIFY
diff: @@ -514,8 +514,13 @@ parse_tsquery(char *buf,
 		return query;
 	}
 
-	/* Pack the QueryItems in the final TSQuery struct to return to caller */
+	if (TSQUERY_TOO_BIG(list_length(state.polstr), state.sumlen))
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("tsquery is too large")));
 	commonlen = COMPUTESIZE(list_length(state.polstr), state.sumlen);
+
+	/* Pack the QueryItems in the final TSQuery struct to return to caller */
 	query = (TSQuery) palloc0(commonlen);
 	SET_VARSIZE(query, commonlen);
 	query->size = list_length(state.polstr);

programming_language: C
filename: tsquery_util.c
change_type: ModificationType.MODIFY
diff: @@ -333,6 +333,11 @@ QTN2QT(QTNode *in)
 	QTN2QTState state;
 
 	cntsize(in, &sumlen, &nnode);
+
+	if (TSQUERY_TOO_BIG(nnode, sumlen))
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("tsquery is too large")));
 	len = COMPUTESIZE(nnode, sumlen);
 
 	out = (TSQuery) palloc0(len);

programming_language: C
filename: txid.c
change_type: ModificationType.MODIFY
diff: @@ -26,7 +26,9 @@
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "libpq/pqformat.h"
+#include "postmaster/postmaster.h"
 #include "utils/builtins.h"
+#include "utils/memutils.h"
 #include "utils/snapmgr.h"
 
 
@@ -66,6 +68,8 @@ typedef struct
 
 #define TXID_SNAPSHOT_SIZE(nxip) \
 	(offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
+#define TXID_SNAPSHOT_MAX_NXIP \
+	((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
 
 /*
  * Epoch values from xact.c
@@ -368,6 +372,13 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
 
 	load_xid_epoch(&state);
 
+	/*
+	 * Compile-time limits on the procarray (MAX_BACKENDS processes plus
+	 * MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
+	 */
+	StaticAssertStmt(MAX_BACKENDS * 2 <= TXID_SNAPSHOT_MAX_NXIP,
+					 "possible overflow in txid_current_snapshot()");
+
 	/* allocate */
 	nxip = cur->xcnt;
 	size = TXID_SNAPSHOT_SIZE(nxip);
@@ -445,20 +456,12 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
 	txid		last = 0;
 	int			nxip;
 	int			i;
-	int			avail;
-	int			expect;
 	txid		xmin,
 				xmax;
 
-	/*
-	 * load nxip and check for nonsense.
-	 *
-	 * (nxip > avail) check is against int overflows in 'expect'.
-	 */
+	/* load and validate nxip */
 	nxip = pq_getmsgint(buf, 4);
-	avail = buf->len - buf->cursor;
-	expect = 8 + 8 + nxip * 8;
-	if (nxip < 0 || nxip > avail || expect > avail)
+	if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
 		goto bad_format;
 
 	xmin = pq_getmsgint64(buf);

programming_language: C
filename: varbit.c
change_type: ModificationType.MODIFY
diff: @@ -148,12 +148,22 @@ bit_in(PG_FUNCTION_ARGS)
 		sp = input_string;
 	}
 
+	/*
+	 * Determine bitlength from input string.  MaxAllocSize ensures a regular
+	 * input is small enough, but we must check hex input.
+	 */
 	slen = strlen(sp);
-	/* Determine bitlength from input string */
 	if (bit_not_hex)
 		bitlen = slen;
 	else
+	{
+		if (slen > VARBITMAXLEN / 4)
+			ereport(ERROR,
+					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("bit string length exceeds the maximum allowed (%d)",
+						VARBITMAXLEN)));
 		bitlen = slen * 4;
+	}
 
 	/*
 	 * Sometimes atttypmod is not supplied. If it is supplied we need to make
@@ -450,12 +460,22 @@ varbit_in(PG_FUNCTION_ARGS)
 		sp = input_string;
 	}
 
+	/*
+	 * Determine bitlength from input string.  MaxAllocSize ensures a regular
+	 * input is small enough, but we must check hex input.
+	 */
 	slen = strlen(sp);
-	/* Determine bitlength from input string */
 	if (bit_not_hex)
 		bitlen = slen;
 	else
+	{
+		if (slen > VARBITMAXLEN / 4)
+			ereport(ERROR,
+					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("bit string length exceeds the maximum allowed (%d)",
+						VARBITMAXLEN)));
 		bitlen = slen * 4;
+	}
 
 	/*
 	 * Sometimes atttypmod is not supplied. If it is supplied we need to make
@@ -535,6 +555,9 @@ varbit_in(PG_FUNCTION_ARGS)
 /*
  * varbit_out -
  *	  Prints the string as bits to preserve length accurately
+ *
+ * XXX varbit_recv() and hex input to varbit_in() can load a value that this
+ * cannot emit.  Consider using hex output for such values.
  */
 Datum
 varbit_out(PG_FUNCTION_ARGS)
@@ -944,6 +967,11 @@ bit_catenate(VarBit *arg1, VarBit *arg2)
 	bitlen1 = VARBITLEN(arg1);
 	bitlen2 = VARBITLEN(arg2);
 
+	if (bitlen1 > VARBITMAXLEN - bitlen2)
+		ereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("bit string length exceeds the maximum allowed (%d)",
+						VARBITMAXLEN)));
 	bytelen = VARBITTOTALLEN(bitlen1 + bitlen2);
 
 	result = (VarBit *) palloc(bytelen);

programming_language: C
filename: ts_type.h
change_type: ModificationType.MODIFY
diff: @@ -13,6 +13,7 @@
 #define _PG_TSTYPE_H_
 
 #include "fmgr.h"
+#include "utils/memutils.h"
 #include "utils/pg_crc.h"
 
 
@@ -244,6 +245,8 @@ typedef TSQueryData *TSQuery;
  * QueryItems, and lenofoperand is the total length of all operands
  */
 #define COMPUTESIZE(size, lenofoperand) ( HDRSIZETQ + (size) * sizeof(QueryItem) + (lenofoperand) )
+#define TSQUERY_TOO_BIG(size, lenofoperand) \
+	((size) > (MaxAllocSize - HDRSIZETQ - (lenofoperand)) / sizeof(QueryItem))
 
 /* Returns a pointer to the first QueryItem in a TSQuery */
 #define GETQUERY(x)  ((QueryItem*)( (char*)(x)+HDRSIZETQ ))

programming_language: C
filename: varbit.h
change_type: ModificationType.MODIFY
diff: @@ -15,6 +15,8 @@
 #ifndef VARBIT_H
 #define VARBIT_H
 
+#include <limits.h>
+
 #include "fmgr.h"
 
 /*
@@ -53,6 +55,11 @@ typedef struct
 /* Number of bytes needed to store a bit string of a given length */
 #define VARBITTOTALLEN(BITLEN)	(((BITLEN) + BITS_PER_BYTE-1)/BITS_PER_BYTE + \
 								 VARHDRSZ + VARBITHDRSZ)
+/*
+ * Maximum number of bits.  Several code sites assume no overflow from
+ * computing bitlen + X; VARBITTOTALLEN() has the largest such X.
+ */
+#define VARBITMAXLEN		(INT_MAX - BITS_PER_BYTE + 1)
 /* pointer beyond the end of the bit string (like end() in STL containers) */
 #define VARBITEND(PTR)		(((bits8 *) (PTR)) + VARSIZE(PTR))
 /* Mask that will cover exactly one byte, i.e. BITS_PER_BYTE bits */

programming_language: C