libpq/SOURCES/postgresql-CVE-2026-6473.patch
2026-06-22 02:16:58 -04:00

1783 lines
54 KiB
Diff

diff --git a/contrib/hstore_plperl/hstore_plperl.c b/contrib/hstore_plperl/hstore_plperl.c
index 417b721..f4c742e 100644
--- a/contrib/hstore_plperl/hstore_plperl.c
+++ b/contrib/hstore_plperl/hstore_plperl.c
@@ -121,7 +121,7 @@ plperl_to_hstore(PG_FUNCTION_ARGS)
pcount = hv_iterinit(hv);
- pairs = palloc(pcount * sizeof(Pairs));
+ pairs = palloc_array(Pairs, pcount);
i = 0;
while ((he = hv_iternext(hv)))
diff --git a/contrib/hstore_plpython/hstore_plpython.c b/contrib/hstore_plpython/hstore_plpython.c
index 372041d..2d14404 100644
--- a/contrib/hstore_plpython/hstore_plpython.c
+++ b/contrib/hstore_plpython/hstore_plpython.c
@@ -153,7 +153,7 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
Py_ssize_t i;
Pairs *pairs;
- pairs = palloc(pcount * sizeof(*pairs));
+ pairs = palloc_array(Pairs, pcount);
for (i = 0; i < pcount; i++)
{
diff --git a/contrib/intarray/_int_bool.c b/contrib/intarray/_int_bool.c
index 4b6a310..d745ee3 100644
--- a/contrib/intarray/_int_bool.c
+++ b/contrib/intarray/_int_bool.c
@@ -436,35 +436,66 @@ boolop(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
+/*
+ * Recursively fill the "left" fields of an ITEM array that represents
+ * a valid postfix tree.
+ *
+ * ptr: starting element of array
+ * pos: in/out argument, the array index this call is responsible to fill
+ *
+ * At exit, *pos has been decremented to point before the sub-tree whose
+ * top is the entry-time value of *pos.
+ */
static void
findoprnd(ITEM *ptr, int32 *pos)
{
+ int32 mypos;
+
/* since this function recurses, it could be driven to stack overflow. */
check_stack_depth();
+ /* get the position this call is supposed to update */
+ mypos = *pos;
+ Assert(mypos >= 0);
+
+ /* in all cases, we should decrement *pos to advance over this item */
+ (*pos)--;
+
#ifdef BS_DEBUG
- elog(DEBUG3, (ptr[*pos].type == OPR) ?
- "%d %c" : "%d %d", *pos, ptr[*pos].val);
+ elog(DEBUG3, (ptr[mypos].type == OPR) ?
+ "%d %c" : "%d %d", mypos, ptr[mypos].val);
#endif
- if (ptr[*pos].type == VAL)
+
+ if (ptr[mypos].type == VAL)
{
- ptr[*pos].left = 0;
- (*pos)--;
+ /* base case: a VAL has no operand, so just set its left to zero */
+ ptr[mypos].left = 0;
}
- else if (ptr[*pos].val == (int32) '!')
+ else if (ptr[mypos].val == (int32) '!')
{
- ptr[*pos].left = -1;
- (*pos)--;
+ /* unary operator, likewise easy: operand is just before it */
+ ptr[mypos].left = -1;
+ /* recurse to scan operand */
findoprnd(ptr, pos);
}
else
{
- ITEM *curitem = &ptr[*pos];
- int32 tmp = *pos;
+ /* binary operator */
+ int32 delta;
- (*pos)--;
+ /* recurse to scan right operand */
findoprnd(ptr, pos);
- curitem->left = *pos - tmp;
+ /* we must fill left with offset to left operand's top */
+ /* abs(delta) < QUERYTYPEMAXITEMS, so it can't overflow ... */
+ delta = *pos - mypos;
+ /* ... but it might be too large to fit in the 16-bit left field */
+ Assert(delta < 0);
+ if (unlikely(delta < PG_INT16_MIN))
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("query_int expression is too complex")));
+ ptr[mypos].left = (int16) delta;
+ /* recurse to scan left operand */
findoprnd(ptr, pos);
}
}
@@ -514,6 +545,7 @@ bqarr_in(PG_FUNCTION_ARGS)
query->size = state.num;
ptr = GETQUERY(query);
+ /* fill the query array from the data makepol constructed */
for (i = state.num - 1; i >= 0; i--)
{
ptr[i].type = state.str->type;
@@ -523,8 +555,12 @@ bqarr_in(PG_FUNCTION_ARGS)
state.str = tmp;
}
+ /* now fill the "left" fields */
pos = query->size - 1;
findoprnd(ptr, &pos);
+ /* if successful, findoprnd should have scanned the whole array */
+ Assert(pos == -1);
+
#ifdef BS_DEBUG
initStringInfo(&pbuf);
for (i = 0; i < query->size; i++)
diff --git a/contrib/ltree/ltree_io.c b/contrib/ltree/ltree_io.c
index 15115cb..9031158 100644
--- a/contrib/ltree/ltree_io.c
+++ b/contrib/ltree/ltree_io.c
@@ -7,6 +7,7 @@
#include <ctype.h>
+#include "common/int.h"
#include "crc32.h"
#include "libpq/pqformat.h"
#include "ltree.h"
@@ -338,7 +339,12 @@ parse_lquery(const char *buf)
lptr++;
lptr->start = ptr;
state = LQPRS_WAITDELIM;
- curqlevel->numvar++;
+ if (pg_add_u16_overflow(curqlevel->numvar, 1, &curqlevel->numvar))
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("lquery level has too many variants"),
+ errdetail("Number of variants exceeds the maximum allowed (%d).",
+ PG_UINT16_MAX)));
}
else
UNCHAR;
@@ -530,7 +536,16 @@ parse_lquery(const char *buf)
lptr = GETVAR(curqlevel);
while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
{
- cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
+ int newlen = cur->totallen + MAXALIGN(LVAR_HDRSIZE + lptr->len);
+
+ if (newlen > PG_UINT16_MAX)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("lquery level is too large"),
+ errdetail("Total size of level exceeds the maximum allowed (%d bytes).",
+ PG_UINT16_MAX)));
+ cur->totallen = (uint16) newlen;
+
lrptr->len = lptr->len;
lrptr->flag = lptr->flag;
lrptr->val = ltree_crc32_sz(lptr->start, lptr->len);
diff --git a/contrib/ltree/ltxtquery_io.c b/contrib/ltree/ltxtquery_io.c
index d967f92..cf774b4 100644
--- a/contrib/ltree/ltxtquery_io.c
+++ b/contrib/ltree/ltxtquery_io.c
@@ -271,31 +271,60 @@ makepol(QPRS_STATE *state)
return END;
}
+/*
+ * Recursively fill the "left" fields of an ITEM array that represents
+ * a valid postfix tree.
+ *
+ * ptr: starting element of array
+ * pos: in/out argument, the array index this call is responsible to fill
+ *
+ * At exit, *pos has been incremented to point after the sub-tree whose
+ * top is the entry-time value of *pos.
+ */
static void
findoprnd(ITEM *ptr, int32 *pos)
{
+ int32 mypos;
+
/* since this function recurses, it could be driven to stack overflow. */
check_stack_depth();
- if (ptr[*pos].type == VAL || ptr[*pos].type == VALTRUE)
+ /* get the position this call is supposed to update */
+ mypos = *pos;
+
+ /* in all cases, we should increment *pos to advance over this item */
+ (*pos)++;
+
+ if (ptr[mypos].type == VAL || ptr[mypos].type == VALTRUE)
{
- ptr[*pos].left = 0;
- (*pos)++;
+ /* base case: a VAL has no operand, so just set its left to zero */
+ ptr[mypos].left = 0;
}
- else if (ptr[*pos].val == (int32) '!')
+ else if (ptr[mypos].val == (int32) '!')
{
- ptr[*pos].left = 1;
- (*pos)++;
+ /* unary operator, likewise easy: operand is just after it */
+ ptr[mypos].left = 1;
+ /* recurse to scan operand */
findoprnd(ptr, pos);
}
else
{
- ITEM *curitem = &ptr[*pos];
- int32 tmp = *pos;
+ /* binary operator */
+ int32 delta;
- (*pos)++;
+ /* recurse to scan right operand */
findoprnd(ptr, pos);
- curitem->left = *pos - tmp;
+ /* we must fill left with offset to left operand's top */
+ /* delta can't overflow, see LTXTQUERY_TOO_BIG ... */
+ delta = *pos - mypos;
+ /* ... but it might be too large to fit in the 16-bit left field */
+ Assert(delta > 0);
+ if (unlikely(delta > PG_INT16_MAX))
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("ltxtquery is too large")));
+ ptr[mypos].left = (int16) delta;
+ /* recurse to scan left operand */
findoprnd(ptr, pos);
}
}
@@ -372,6 +401,8 @@ queryin(char *buf)
/* set left operand's position for every operator */
pos = 0;
findoprnd(ptr, &pos);
+ /* if successful, findoprnd should have scanned the whole array */
+ Assert(pos == state.num);
return query;
}
diff --git a/src/backend/regex/regc_color.c b/src/backend/regex/regc_color.c
index f5a4151..70e7e28 100644
--- a/src/backend/regex/regc_color.c
+++ b/src/backend/regex/regc_color.c
@@ -218,6 +218,7 @@ newcolor(struct colormap *cm)
n = cm->ncds * 2;
if (n > MAX_COLOR + 1)
n = MAX_COLOR + 1;
+ /* the MAX_COLOR+1 limit ensures these alloc sizes can't overflow: */
if (cm->cd == cm->cdspace)
{
newCd = (struct colordesc *) MALLOC(n * sizeof(struct colordesc));
@@ -434,9 +435,8 @@ newhicolorrow(struct colormap *cm,
CERR(REG_ESPACE);
return 0;
}
- newarray = (color *) REALLOC(cm->hicolormap,
- cm->maxarrayrows * 2 *
- cm->hiarraycols * sizeof(color));
+ newarray = REALLOC_ARRAY(cm->hicolormap, color,
+ cm->maxarrayrows * 2 * cm->hiarraycols);
if (newarray == NULL)
{
CERR(REG_ESPACE);
@@ -477,9 +477,8 @@ newhicolorcols(struct colormap *cm)
CERR(REG_ESPACE);
return;
}
- newarray = (color *) REALLOC(cm->hicolormap,
- cm->maxarrayrows *
- cm->hiarraycols * 2 * sizeof(color));
+ newarray = REALLOC_ARRAY(cm->hicolormap, color,
+ cm->maxarrayrows * cm->hiarraycols * 2);
if (newarray == NULL)
{
CERR(REG_ESPACE);
@@ -652,8 +651,7 @@ subcoloronechr(struct vars *v,
* Potentially, we could need two more colormapranges than we have now, if
* the given chr is in the middle of some existing range.
*/
- newranges = (colormaprange *)
- MALLOC((cm->numcmranges + 2) * sizeof(colormaprange));
+ newranges = MALLOC_ARRAY(colormaprange, cm->numcmranges + 2);
if (newranges == NULL)
{
CERR(REG_ESPACE);
@@ -766,8 +764,7 @@ subcoloronerange(struct vars *v,
* Potentially, if we have N non-adjacent ranges, we could need as many as
* 2N+1 result ranges (consider case where new range spans 'em all).
*/
- newranges = (colormaprange *)
- MALLOC((cm->numcmranges * 2 + 1) * sizeof(colormaprange));
+ newranges = MALLOC_ARRAY(colormaprange, cm->numcmranges * 2 + 1);
if (newranges == NULL)
{
CERR(REG_ESPACE);
diff --git a/src/backend/regex/regc_cvec.c b/src/backend/regex/regc_cvec.c
index 1030621..8dbcf3c 100644
--- a/src/backend/regex/regc_cvec.c
+++ b/src/backend/regex/regc_cvec.c
@@ -40,6 +40,9 @@
/*
* newcvec - allocate a new cvec
+ *
+ * Note: in current usage, nchrs and nranges are never so large that we risk
+ * integer overflow in these size calculations, even with 32-bit size_t.
*/
static struct cvec *
newcvec(int nchrs, /* to hold this many chrs... */
diff --git a/src/backend/regex/regc_nfa.c b/src/backend/regex/regc_nfa.c
index 3c65d97..8ce89c4 100644
--- a/src/backend/regex/regc_nfa.c
+++ b/src/backend/regex/regc_nfa.c
@@ -2849,6 +2849,10 @@ compact(struct nfa *nfa,
assert(!NISERR());
+ /*
+ * The REG_MAX_COMPILE_SPACE restriction ensures that integer overflow
+ * can't occur in this loop nor in the allocation requests below.
+ */
nstates = 0;
narcs = 0;
for (s = nfa->states; s != NULL; s = s->next)
@@ -2898,6 +2902,13 @@ compact(struct nfa *nfa,
break;
case LACON:
assert(s->no != cnfa->pre);
+ assert(a->co >= 0);
+ /* make sure the modified color number will fit */
+ if (a->co > MAX_COLOR - cnfa->ncolors)
+ {
+ NERR(REG_ECOLORS);
+ return;
+ }
ca->co = (color) (cnfa->ncolors + a->co);
ca->to = a->to->no;
ca++;
diff --git a/src/backend/regex/regcomp.c b/src/backend/regex/regcomp.c
index fba4462..f90e727 100644
--- a/src/backend/regex/regcomp.c
+++ b/src/backend/regex/regcomp.c
@@ -499,6 +499,7 @@ moresubs(struct vars *v,
assert(wanted > 0 && (size_t) wanted >= v->nsubs);
n = (size_t) wanted * 3 / 2 + 1;
+ /* n is bounded by the number of states, so no chance of overflow here */
if (v->subs == v->sub10)
{
p = (struct subre **) MALLOC(n * sizeof(struct subre *));
@@ -1952,8 +1953,8 @@ newlacon(struct vars *v,
else
{
n = v->nlacons;
- newlacons = (struct subre *) REALLOC(v->lacons,
- (n + 1) * sizeof(struct subre));
+ /* better use REALLOC_ARRAY here, as struct subre is big */
+ newlacons = REALLOC_ARRAY(v->lacons, struct subre, n + 1);
}
if (newlacons == NULL)
{
diff --git a/src/backend/regex/rege_dfa.c b/src/backend/regex/rege_dfa.c
index 5695e15..7cce6cb 100644
--- a/src/backend/regex/rege_dfa.c
+++ b/src/backend/regex/rege_dfa.c
@@ -469,20 +469,29 @@ newdfa(struct vars *v,
}
else
{
+ /*
+ * Restrict the ranges of nstates and ncolors enough that the arrays
+ * we allocate here have no more than INT_MAX members. This protects
+ * not only the allocation calculations just below, but later indexing
+ * into these arrays.
+ */
+ if (wordsper >= INT_MAX / (nss + WORK) ||
+ cnfa->ncolors >= INT_MAX / nss)
+ {
+ ERR(REG_ETOOBIG);
+ return NULL;
+ }
d = (struct dfa *) MALLOC(sizeof(struct dfa));
if (d == NULL)
{
ERR(REG_ESPACE);
return NULL;
}
- d->ssets = (struct sset *) MALLOC(nss * sizeof(struct sset));
- d->statesarea = (unsigned *) MALLOC((nss + WORK) * wordsper *
- sizeof(unsigned));
+ d->ssets = MALLOC_ARRAY(struct sset, nss);
+ d->statesarea = MALLOC_ARRAY(unsigned, (nss + WORK) * wordsper);
d->work = &d->statesarea[nss * wordsper];
- d->outsarea = (struct sset **) MALLOC(nss * cnfa->ncolors *
- sizeof(struct sset *));
- d->incarea = (struct arcp *) MALLOC(nss * cnfa->ncolors *
- sizeof(struct arcp));
+ d->outsarea = MALLOC_ARRAY(struct sset *, nss * cnfa->ncolors);
+ d->incarea = MALLOC_ARRAY(struct arcp, nss * cnfa->ncolors);
d->cptsmalloced = 1;
d->mallocarea = (char *) d;
if (d->ssets == NULL || d->statesarea == NULL ||
diff --git a/src/backend/regex/regexec.c b/src/backend/regex/regexec.c
index 460c987..d876aa6 100644
--- a/src/backend/regex/regexec.c
+++ b/src/backend/regex/regexec.c
@@ -220,8 +220,7 @@ pg_regexec(regex_t *re,
if (v->g->nsub + 1 <= LOCALMAT)
v->pmatch = mat;
else
- v->pmatch = (regmatch_t *) MALLOC((v->g->nsub + 1) *
- sizeof(regmatch_t));
+ v->pmatch = MALLOC_ARRAY(regmatch_t, v->g->nsub + 1);
if (v->pmatch == NULL)
return REG_ESPACE;
v->nmatch = v->g->nsub + 1;
@@ -247,6 +246,7 @@ pg_regexec(regex_t *re,
v->subdfas = subdfas;
else
{
+ /* ntree is surely less than the number of states, so this is safe: */
v->subdfas = (struct dfa **) MALLOC(n * sizeof(struct dfa *));
if (v->subdfas == NULL)
{
@@ -261,6 +261,7 @@ pg_regexec(regex_t *re,
n = (size_t) v->g->nlacons;
if (n > 0)
{
+ /* nlacons is surely less than the number of arcs, so this is safe: */
v->ladfas = (struct dfa **) MALLOC(n * sizeof(struct dfa *));
if (v->ladfas == NULL)
{
@@ -1107,7 +1108,7 @@ citerdissect(struct vars *v,
max_matches = t->max;
if (max_matches < min_matches)
max_matches = min_matches;
- endpts = (chr **) MALLOC((max_matches + 1) * sizeof(chr *));
+ endpts = MALLOC_ARRAY(chr *, max_matches + 1);
if (endpts == NULL)
return REG_ESPACE;
endpts[0] = begin;
@@ -1310,7 +1311,7 @@ creviterdissect(struct vars *v,
max_matches = t->max;
if (max_matches < min_matches)
max_matches = min_matches;
- endpts = (chr **) MALLOC((max_matches + 1) * sizeof(chr *));
+ endpts = MALLOC_ARRAY(chr *, max_matches + 1);
if (endpts == NULL)
return REG_ESPACE;
endpts[0] = begin;
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 97716f6..6c9075f 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -491,42 +491,6 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
}
-/*
- * Add two Size values, checking for overflow
- */
-Size
-add_size(Size s1, Size s2)
-{
- Size result;
-
- result = s1 + s2;
- /* We are assuming Size is an unsigned type here... */
- if (result < s1 || result < s2)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("requested shared memory size overflows size_t")));
- return result;
-}
-
-/*
- * Multiply two Size values, checking for overflow
- */
-Size
-mul_size(Size s1, Size s2)
-{
- Size result;
-
- if (s1 == 0 || s2 == 0)
- return 0;
- result = s1 * s2;
- /* We are assuming Size is an unsigned type here... */
- if (result / s2 != s1)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("requested shared memory size overflows size_t")));
- return result;
-}
-
/* SQL SRF showing allocated shared memory */
Datum
pg_get_shmem_allocations(PG_FUNCTION_ARGS)
diff --git a/src/backend/tsearch/wparser_def.c b/src/backend/tsearch/wparser_def.c
index af97b5b..eaa88b0 100644
--- a/src/backend/tsearch/wparser_def.c
+++ b/src/backend/tsearch/wparser_def.c
@@ -2563,6 +2563,9 @@ prsd_headline(PG_FUNCTION_ARGS)
bool highlightall = false;
int max_cover;
ListCell *l;
+ size_t startsellen;
+ size_t stopsellen;
+ size_t fragdelimlen;
/* Extract configuration option values */
prs->startsel = NULL;
@@ -2648,9 +2651,24 @@ prsd_headline(PG_FUNCTION_ARGS)
prs->fragdelim = pstrdup(" ... ");
/* Caller will need these lengths, too */
- prs->startsellen = strlen(prs->startsel);
- prs->stopsellen = strlen(prs->stopsel);
- prs->fragdelimlen = strlen(prs->fragdelim);
+ startsellen = strlen(prs->startsel);
+ stopsellen = strlen(prs->stopsel);
+ fragdelimlen = strlen(prs->fragdelim);
+ if (startsellen > PG_INT16_MAX)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("value for \"%s\" is too long", "StartSel")));
+ if (stopsellen > PG_INT16_MAX)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("value for \"%s\" is too long", "StopSel")));
+ if (fragdelimlen > PG_INT16_MAX)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("value for \"%s\" is too long", "FragmentDelimiter")));
+ prs->startsellen = startsellen;
+ prs->stopsellen = stopsellen;
+ prs->fragdelimlen = fragdelimlen;
PG_RETURN_POINTER(prs);
}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 5e9e680..aa3a545 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -5320,6 +5320,7 @@ accumArrayResultArr(ArrayBuildStateArr *astate,
ndatabytes;
char *data;
int i;
+ int newnitems;
/*
* We disallow accumulating null subarrays. Another plausible definition
@@ -5349,6 +5350,14 @@ accumArrayResultArr(ArrayBuildStateArr *astate,
nitems = ArrayGetNItems(ndims, dims);
ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
+ /* Check that the array doesn't grow too large */
+ newnitems = astate->nitems + nitems;
+ if (newnitems > MaxArraySize)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("array size exceeds the maximum allowed (%zu)",
+ MaxArraySize)));
+
if (astate->ndims == 0)
{
/* First input; check/save the dimensionality info */
@@ -5414,8 +5423,6 @@ accumArrayResultArr(ArrayBuildStateArr *astate,
/* Deal with null bitmap if needed */
if (astate->nullbitmap || ARR_HASNULL(arg))
{
- int newnitems = astate->nitems + nitems;
-
if (astate->nullbitmap == NULL)
{
/*
@@ -5439,7 +5446,7 @@ accumArrayResultArr(ArrayBuildStateArr *astate,
nitems);
}
- astate->nitems += nitems;
+ astate->nitems = newnitems;
astate->dims[0] += 1;
MemoryContextSwitchTo(oldcontext);
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 47bef08..69382b7 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -4038,7 +4038,7 @@ datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval, Oid collid)
/*
* Allocate workspace for result as C string
*/
- result = palloc((fmt_len * DCH_MAX_ITEM_SIZ) + 1);
+ result = palloc(mul_size(fmt_len, DCH_MAX_ITEM_SIZ) + 1);
*result = '\0';
if (fmt_len > DCH_CACHE_SIZE)
@@ -4049,7 +4049,7 @@ datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval, Oid collid)
*/
incache = false;
- format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
+ format = palloc_array(FormatNode, fmt_len + 1);
parse_format(format, fmt_str, DCH_keywords,
DCH_suff, DCH_index, DCH_FLAG, NULL);
@@ -4505,7 +4505,7 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
* Allocate new memory if format picture is bigger than static
* cache and do not use cache (call parser always)
*/
- format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
+ format = palloc_array(FormatNode, fmt_len + 1);
parse_format(format, fmt_str, DCH_keywords, DCH_suff, DCH_index,
DCH_FLAG | (std ? STD_FLAG : 0), NULL);
@@ -4945,7 +4945,7 @@ NUM_cache(int len, NUMDesc *Num, text *pars_str, bool *shouldFree)
* Allocate new memory if format picture is bigger than static cache
* and do not use cache (call parser always)
*/
- format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));
+ format = palloc_array(FormatNode, len + 1);
*shouldFree = true;
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 3c4fde6..afdc620 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -1833,7 +1833,7 @@ icu_to_uchar(UChar **buff_uchar, const char *buff, size_t nbytes)
ereport(ERROR,
(errmsg("%s failed: %s", "ucnv_toUChars", u_errorName(status))));
- *buff_uchar = palloc((len_uchar + 1) * sizeof(**buff_uchar));
+ *buff_uchar = palloc_array(UChar, len_uchar + 1);
status = U_ZERO_ERROR;
len_uchar = ucnv_toUChars(icu_converter, *buff_uchar, len_uchar + 1,
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 9dea2a5..26f86c0 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -6045,18 +6045,18 @@ unicode_normalize_func(PG_FUNCTION_ARGS)
text *input = PG_GETARG_TEXT_PP(0);
char *formstr = text_to_cstring(PG_GETARG_TEXT_PP(1));
UnicodeNormalizationForm form;
- int size;
+ size_t size;
pg_wchar *input_chars;
pg_wchar *output_chars;
unsigned char *p;
text *result;
- int i;
+ size_t i;
form = unicode_norm_form_from_string(formstr);
/* convert to pg_wchar */
size = pg_mbstrlen_with_len(VARDATA_ANY(input), VARSIZE_ANY_EXHDR(input));
- input_chars = palloc((size + 1) * sizeof(pg_wchar));
+ input_chars = palloc_array(pg_wchar, size + 1);
p = (unsigned char *) VARDATA_ANY(input);
for (i = 0; i < size; i++)
{
@@ -6111,20 +6111,20 @@ unicode_is_normalized(PG_FUNCTION_ARGS)
text *input = PG_GETARG_TEXT_PP(0);
char *formstr = text_to_cstring(PG_GETARG_TEXT_PP(1));
UnicodeNormalizationForm form;
- int size;
+ size_t size;
pg_wchar *input_chars;
pg_wchar *output_chars;
unsigned char *p;
- int i;
+ size_t i;
UnicodeNormalizationQC quickcheck;
- int output_size;
+ size_t output_size;
bool result;
form = unicode_norm_form_from_string(formstr);
/* convert to pg_wchar */
size = pg_mbstrlen_with_len(VARDATA_ANY(input), VARSIZE_ANY_EXHDR(input));
- input_chars = palloc((size + 1) * sizeof(pg_wchar));
+ input_chars = palloc_array(pg_wchar, size + 1);
p = (unsigned char *) VARDATA_ANY(input);
for (i = 0; i < size; i++)
{
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index abda22f..3bb21fc 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -21,6 +21,7 @@
#include "postgres.h"
+#include "common/int.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "utils/memdebug.h"
@@ -58,6 +59,8 @@ static void MemoryContextStatsInternal(MemoryContext context, int level,
MemoryContextCounters *totals);
static void MemoryContextStatsPrint(MemoryContext context, void *passthru,
const char *stats_string);
+static pg_noinline void add_size_error(Size s1, Size s2) pg_attribute_noreturn();
+static pg_noinline void mul_size_error(Size s1, Size s2) pg_attribute_noreturn();
/*
* You should not do memory allocations within a critical section, because
@@ -1095,6 +1098,172 @@ repalloc(void *pointer, Size size)
return ret;
}
+/*
+ * repalloc_extended
+ * Adjust the size of a previously allocated chunk,
+ * with HUGE and NO_OOM options.
+ */
+void *
+repalloc_extended(void *pointer, Size size, int flags)
+{
+ MemoryContext context = GetMemoryChunkContext(pointer);
+ void *ret;
+
+ if (!((flags & MCXT_ALLOC_HUGE) != 0 ? AllocHugeSizeIsValid(size) :
+ AllocSizeIsValid(size)))
+ elog(ERROR, "invalid memory alloc request size %zu", size);
+
+ AssertNotInCriticalSection(context);
+
+ /* isReset must be false already */
+ Assert(!context->isReset);
+
+ ret = context->methods->realloc(context, pointer, size);
+ if (unlikely(ret == NULL))
+ {
+ if ((flags & MCXT_ALLOC_NO_OOM) == 0)
+ {
+ MemoryContextStats(TopMemoryContext);
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of memory"),
+ errdetail("Failed on request of size %zu in memory context \"%s\".",
+ size, context->name)));
+ }
+ return NULL;
+ }
+
+ VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
+
+ return ret;
+}
+
+/*
+ * Support for safe calculation of memory request sizes
+ *
+ * These functions perform the requested calculation, but throw error if the
+ * result overflows.
+ *
+ * An important property of these functions is that if an argument was a
+ * negative signed int before promotion (implying overflow in calculating it)
+ * we will detect that as an error. That happens because we reject results
+ * larger than SIZE_MAX / 2 later on, in the actual allocation step.
+ */
+Size
+add_size(Size s1, Size s2)
+{
+ Size result;
+
+ if (unlikely(pg_add_size_overflow(s1, s2, &result)))
+ add_size_error(s1, s2);
+ return result;
+}
+
+static pg_noinline void
+add_size_error(Size s1, Size s2)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("invalid memory allocation request size %zu + %zu",
+ s1, s2)));
+}
+
+Size
+mul_size(Size s1, Size s2)
+{
+ Size result;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &result)))
+ mul_size_error(s1, s2);
+ return result;
+}
+
+static pg_noinline void
+mul_size_error(Size s1, Size s2)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("invalid memory allocation request size %zu * %zu",
+ s1, s2)));
+}
+
+/*
+ * palloc_mul
+ * Equivalent to palloc(mul_size(s1, s2)).
+ */
+void *
+palloc_mul(Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req)))
+ mul_size_error(s1, s2);
+ return palloc(req);
+}
+
+/*
+ * palloc0_mul
+ * Equivalent to palloc0(mul_size(s1, s2)).
+ *
+ * This is comparable to standard calloc's behavior.
+ */
+void *
+palloc0_mul(Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req)))
+ mul_size_error(s1, s2);
+ return palloc0(req);
+}
+
+/*
+ * palloc_mul_extended
+ * Equivalent to palloc_extended(mul_size(s1, s2), flags).
+ */
+void *
+palloc_mul_extended(Size s1, Size s2, int flags)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req)))
+ mul_size_error(s1, s2);
+ return palloc_extended(req, flags);
+}
+
+/*
+ * repalloc_mul
+ * Equivalent to repalloc(p, mul_size(s1, s2)).
+ */
+void *
+repalloc_mul(void *p, Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req)))
+ mul_size_error(s1, s2);
+ return repalloc(p, req);
+}
+
+/*
+ * repalloc_mul_extended
+ * Equivalent to repalloc_extended(p, mul_size(s1, s2), flags).
+ */
+void *
+repalloc_mul_extended(void *p, Size s1, Size s2, int flags)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req)))
+ mul_size_error(s1, s2);
+ return repalloc_extended(p, req, flags);
+}
+
/*
* MemoryContextAllocHuge
* Allocate (possibly-expansive) space within the specified context.
@@ -1138,31 +1307,8 @@ MemoryContextAllocHuge(MemoryContext context, Size size)
void *
repalloc_huge(void *pointer, Size size)
{
- MemoryContext context = GetMemoryChunkContext(pointer);
- void *ret;
-
- if (!AllocHugeSizeIsValid(size))
- elog(ERROR, "invalid memory alloc request size %zu", size);
-
- AssertNotInCriticalSection(context);
-
- /* isReset must be false already */
- Assert(!context->isReset);
-
- ret = context->methods->realloc(context, pointer, size);
- if (unlikely(ret == NULL))
- {
- MemoryContextStats(TopMemoryContext);
- ereport(ERROR,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("out of memory"),
- errdetail("Failed on request of size %zu in memory context \"%s\".",
- size, context->name)));
- }
-
- VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);
-
- return ret;
+ /* this one seems not worth its own implementation */
+ return repalloc_extended(pointer, size, MCXT_ALLOC_HUGE);
}
/*
diff --git a/src/common/fe_memutils.c b/src/common/fe_memutils.c
index b027a02..a902aa6 100644
--- a/src/common/fe_memutils.c
+++ b/src/common/fe_memutils.c
@@ -19,6 +19,12 @@
#include "postgres_fe.h"
+#include "common/int.h"
+
+static pg_noinline void add_size_error(Size s1, Size s2) pg_attribute_noreturn();
+static pg_noinline void mul_size_error(Size s1, Size s2) pg_attribute_noreturn();
+
+
static inline void *
pg_malloc_internal(size_t size, int flags)
{
@@ -174,3 +180,185 @@ repalloc(void *pointer, Size size)
{
return pg_realloc(pointer, size);
}
+
+/*
+ * Support for safe calculation of memory request sizes
+ *
+ * These functions perform the requested calculation, but throw error if the
+ * result overflows.
+ *
+ * An important property of these functions is that if an argument was a
+ * negative signed int before promotion (implying overflow in calculating it)
+ * we will detect that as an error. That happens because we reject results
+ * larger than SIZE_MAX / 2. In the backend we rely on later checks to do
+ * that, but in frontend we must do it here.
+ */
+Size
+add_size(Size s1, Size s2)
+{
+ Size result;
+
+ if (unlikely(pg_add_size_overflow(s1, s2, &result) ||
+ result > (SIZE_MAX / 2)))
+ add_size_error(s1, s2);
+ return result;
+}
+
+static pg_noinline void
+add_size_error(Size s1, Size s2)
+{
+ fprintf(stderr, _("invalid memory allocation request size %zu + %zu\n"),
+ s1, s2);
+ exit(EXIT_FAILURE);
+}
+
+Size
+mul_size(Size s1, Size s2)
+{
+ Size result;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &result) ||
+ result > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return result;
+}
+
+static pg_noinline void
+mul_size_error(Size s1, Size s2)
+{
+ fprintf(stderr, _("invalid memory allocation request size %zu * %zu\n"),
+ s1, s2);
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * pg_malloc_mul
+ * Equivalent to pg_malloc(mul_size(s1, s2)).
+ */
+void *
+pg_malloc_mul(Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return pg_malloc(req);
+}
+
+/*
+ * pg_malloc0_mul
+ * Equivalent to pg_malloc0(mul_size(s1, s2)).
+ *
+ * This is comparable to standard calloc's behavior.
+ */
+void *
+pg_malloc0_mul(Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return pg_malloc0(req);
+}
+
+/*
+ * pg_malloc_mul_extended
+ * Equivalent to pg_malloc_extended(mul_size(s1, s2), flags).
+ */
+void *
+pg_malloc_mul_extended(Size s1, Size s2, int flags)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return pg_malloc_extended(req, flags);
+}
+
+/*
+ * pg_realloc_mul
+ * Equivalent to pg_realloc(p, mul_size(s1, s2)).
+ */
+void *
+pg_realloc_mul(void *p, Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return pg_realloc(p, req);
+}
+
+/*
+ * palloc_mul
+ * Equivalent to palloc(mul_size(s1, s2)).
+ */
+void *
+palloc_mul(Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return palloc(req);
+}
+
+/*
+ * palloc0_mul
+ * Equivalent to palloc0(mul_size(s1, s2)).
+ *
+ * This is comparable to standard calloc's behavior.
+ */
+void *
+palloc0_mul(Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return palloc0(req);
+}
+
+/*
+ * palloc_mul_extended
+ * Equivalent to palloc_extended(mul_size(s1, s2), flags).
+ */
+void *
+palloc_mul_extended(Size s1, Size s2, int flags)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return palloc_extended(req, flags);
+}
+
+/*
+ * repalloc_mul
+ * Equivalent to repalloc(p, mul_size(s1, s2)).
+ */
+void *
+repalloc_mul(void *p, Size s1, Size s2)
+{
+ /* inline mul_size() for efficiency */
+ Size req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req) ||
+ req > (SIZE_MAX / 2)))
+ mul_size_error(s1, s2);
+ return repalloc(p, req);
+}
diff --git a/src/common/psprintf.c b/src/common/psprintf.c
index 4d9d15e..1d05fc1 100644
--- a/src/common/psprintf.c
+++ b/src/common/psprintf.c
@@ -24,9 +24,6 @@
#include "postgres_fe.h"
-/* It's possible we could use a different value for this in frontend code */
-#define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
-
#endif
diff --git a/src/common/stringinfo.c b/src/common/stringinfo.c
index 0badc46..11f5017 100644
--- a/src/common/stringinfo.c
+++ b/src/common/stringinfo.c
@@ -24,9 +24,6 @@
#include "postgres_fe.h"
-/* It's possible we could use a different value for this in frontend code */
-#define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
-
#endif
#include "lib/stringinfo.h"
diff --git a/src/common/unicode_norm.c b/src/common/unicode_norm.c
index cfea34e..437aed4 100644
--- a/src/common/unicode_norm.c
+++ b/src/common/unicode_norm.c
@@ -22,6 +22,7 @@
#include "common/unicode_norm_table.h"
#ifndef FRONTEND
#include "common/unicode_normprops_table.h"
+#include "utils/memutils.h"
#endif
#ifndef FRONTEND
@@ -330,10 +331,28 @@ unicode_normalize(UnicodeNormalizationForm form, const pg_wchar *input)
/*
* Calculate how many characters long the decomposed version will be.
+ *
+ * Some characters decompose to quite a few code points, so that the
+ * decomposed version's size could overrun MaxAllocSize, and even 32-bit
+ * size_t, even though the input string presumably fits in that. In
+ * frontend we want to just return NULL in that case, so monitor the sum
+ * and exit early once we'd need more than MaxAllocSize bytes.
*/
decomp_size = 0;
for (p = input; *p; p++)
+ {
decomp_size += get_decomposed_size(*p, compat);
+ if (unlikely(decomp_size > MaxAllocSize / sizeof(pg_wchar)))
+ {
+#ifndef FRONTEND
+ /* Exit loop and let palloc() throw error below */
+ break;
+#else
+ /* Just return NULL with no explicit error */
+ return NULL;
+#endif
+ }
+ }
decomp_chars = (pg_wchar *) ALLOC((decomp_size + 1) * sizeof(pg_wchar));
if (decomp_chars == NULL)
diff --git a/src/include/common/fe_memutils.h b/src/include/common/fe_memutils.h
index 85f3c40..5002f89 100644
--- a/src/include/common/fe_memutils.h
+++ b/src/include/common/fe_memutils.h
@@ -9,6 +9,18 @@
#ifndef FE_MEMUTILS_H
#define FE_MEMUTILS_H
+/*
+ * Assumed maximum size for allocation requests.
+ *
+ * We don't enforce this, so the actual maximum is the platform's SIZE_MAX.
+ * But it's useful to have it defined in frontend builds, so that common
+ * code can check for oversized requests without having frontend-vs-backend
+ * differences. Also, some code relies on MaxAllocSize being no more than
+ * INT_MAX/2, so rather than setting this to SIZE_MAX, make it the same as
+ * the backend's value.
+ */
+#define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
+
/*
* Flags for pg_malloc_extended and palloc_extended, deliberately named
* the same as the backend flags.
@@ -29,6 +41,16 @@ extern void *pg_malloc_extended(size_t size, int flags);
extern void *pg_realloc(void *pointer, size_t size);
extern void pg_free(void *pointer);
+/*
+ * Support for safe calculation of memory request sizes
+ */
+extern Size add_size(Size s1, Size s2);
+extern Size mul_size(Size s1, Size s2);
+extern void *pg_malloc_mul(Size s1, Size s2);
+extern void *pg_malloc0_mul(Size s1, Size s2);
+extern void *pg_malloc_mul_extended(Size s1, Size s2, int flags);
+extern void *pg_realloc_mul(void *p, Size s1, Size s2);
+
/*
* Variants with easier notation and more type safety
*/
@@ -42,14 +64,15 @@ extern void pg_free(void *pointer);
/*
* Allocate space for "count" objects of type "type"
*/
-#define pg_malloc_array(type, count) ((type *) pg_malloc(sizeof(type) * (count)))
-#define pg_malloc0_array(type, count) ((type *) pg_malloc0(sizeof(type) * (count)))
+#define pg_malloc_array(type, count) ((type *) pg_malloc_mul(sizeof(type), count))
+#define pg_malloc0_array(type, count) ((type *) pg_malloc0_mul(sizeof(type), count))
+#define pg_malloc_array_extended(type, count, flags) ((type *) pg_malloc_mul_extended(sizeof(type), count, flags))
/*
* Change size of allocation pointed to by "pointer" to have space for "count"
* objects of type "type"
*/
-#define pg_realloc_array(pointer, type, count) ((type *) pg_realloc(pointer, sizeof(type) * (count)))
+#define pg_realloc_array(pointer, type, count) ((type *) pg_realloc_mul(pointer, sizeof(type), count))
/* Equivalent functions, deliberately named the same as backend functions */
extern char *pstrdup(const char *in);
@@ -59,12 +82,17 @@ extern void *palloc0(Size size);
extern void *palloc_extended(Size size, int flags);
extern void *repalloc(void *pointer, Size size);
extern void pfree(void *pointer);
+extern void *palloc_mul(Size s1, Size s2);
+extern void *palloc0_mul(Size s1, Size s2);
+extern void *palloc_mul_extended(Size s1, Size s2, int flags);
+extern void *repalloc_mul(void *p, Size s1, Size s2);
#define palloc_object(type) ((type *) palloc(sizeof(type)))
#define palloc0_object(type) ((type *) palloc0(sizeof(type)))
-#define palloc_array(type, count) ((type *) palloc(sizeof(type) * (count)))
-#define palloc0_array(type, count) ((type *) palloc0(sizeof(type) * (count)))
-#define repalloc_array(pointer, type, count) ((type *) repalloc(pointer, sizeof(type) * (count)))
+#define palloc_array(type, count) ((type *) palloc_mul(sizeof(type), count))
+#define palloc0_array(type, count) ((type *) palloc0_mul(sizeof(type), count))
+#define palloc_array_extended(type, count, flags) ((type *) palloc_mul_extended(sizeof(type), count, flags))
+#define repalloc_array(pointer, type, count) ((type *) repalloc_mul(pointer, sizeof(type), count))
/* sprintf into a palloc'd buffer --- these are in psprintf.c */
extern char *psprintf(const char *fmt,...) pg_attribute_printf(1, 2);
diff --git a/src/include/common/int.h b/src/include/common/int.h
index 4c86265..ea74b48 100644
--- a/src/include/common/int.h
+++ b/src/include/common/int.h
@@ -438,4 +438,71 @@ pg_mul_u64_overflow(uint64 a, uint64 b, uint64 *result)
#endif
}
+/*
+ * size_t
+ */
+static inline bool
+pg_add_size_overflow(size_t a, size_t b, size_t *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+ return __builtin_add_overflow(a, b, result);
+#else
+ size_t res = a + b;
+
+ if (res < a)
+ {
+ *result = 0x5EED; /* to avoid spurious warnings */
+ return true;
+ }
+ *result = res;
+ return false;
+#endif
+}
+
+static inline bool
+pg_sub_size_overflow(size_t a, size_t b, size_t *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+ return __builtin_sub_overflow(a, b, result);
+#else
+ if (b > a)
+ {
+ *result = 0x5EED; /* to avoid spurious warnings */
+ return true;
+ }
+ *result = a - b;
+ return false;
+#endif
+}
+
+static inline bool
+pg_mul_size_overflow(size_t a, size_t b, size_t *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+ return __builtin_mul_overflow(a, b, result);
+#else
+ size_t res = a * b;
+
+ if (a != 0 && b != res / a)
+ {
+ *result = 0x5EED; /* to avoid spurious warnings */
+ return true;
+ }
+ *result = res;
+ return false;
+#endif
+}
+
+/*
+ * pg_neg_size_overflow is currently omitted, to avoid having to reason about
+ * the portability of SSIZE_MIN/_MAX before a use case exists.
+ */
+/*
+ * static inline bool
+ * pg_neg_size_overflow(size_t a, ssize_t *result)
+ * {
+ * ...
+ * }
+ */
+
#endif /* COMMON_INT_H */
diff --git a/src/include/regex/regcustom.h b/src/include/regex/regcustom.h
index 100c52d..fd6595b 100644
--- a/src/include/regex/regcustom.h
+++ b/src/include/regex/regcustom.h
@@ -50,8 +50,8 @@
#include <wctype.h>
#endif
+#include "common/int.h"
#include "mb/pg_wchar.h"
-
#include "miscadmin.h" /* needed by rcancelrequested/rstacktoodeep */
@@ -60,8 +60,32 @@
#define MALLOC(n) malloc(n)
#define FREE(p) free(VS(p))
#define REALLOC(p,n) realloc(VS(p),n)
+#define MALLOC_ARRAY(type, n) \
+ ((type *) pg_regex_malloc_array(sizeof(type), n))
+#define REALLOC_ARRAY(p, type, n) \
+ ((type *) pg_regex_realloc_array(p, sizeof(type), n))
#define assert(x) Assert(x)
+static inline void *
+pg_regex_malloc_array(size_t s1, size_t s2)
+{
+ size_t req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req)))
+ return NULL;
+ return malloc(req);
+}
+
+static inline void *
+pg_regex_realloc_array(void *p, size_t s1, size_t s2)
+{
+ size_t req;
+
+ if (unlikely(pg_mul_size_overflow(s1, s2, &req)))
+ return NULL;
+ return realloc(p, req);
+}
+
/* internal character type and related */
typedef pg_wchar chr; /* the type itself */
typedef unsigned uchr; /* unsigned type that will hold a chr */
diff --git a/src/include/regex/regguts.h b/src/include/regex/regguts.h
index 5d0e7a9..ff652e3 100644
--- a/src/include/regex/regguts.h
+++ b/src/include/regex/regguts.h
@@ -76,6 +76,14 @@
#ifndef FREE
#define FREE(p) free(VS(p))
#endif
+#ifndef MALLOC_ARRAY
+/* we don't depend on calloc's zeroing behavior, we do need overflow check */
+#define MALLOC_ARRAY(type, n) ((type *) calloc(sizeof(type), n))
+#endif
+#ifndef REALLOC_ARRAY
+/* XXX this definition does not provide the desired overflow check */
+#define REALLOC_ARRAY(p, type, n) ((type *) REALLOC(p, sizeof(type) * (n)))
+#endif
/* want size of a char in bits, and max value in bounded quantifiers */
#ifndef _POSIX2_RE_DUP_MAX
@@ -378,6 +386,11 @@ struct cnfa
* transient data is generally not large enough to notice compared to those.
* Note that we do not charge anything for the final output data structures
* (the compacted NFA and the colormap).
+ *
+ * Do not raise this so high as to allow more than INT_MAX/8 states or arcs,
+ * or you risk integer overflows in various space allocation requests.
+ * (We could be more defensive in those places, but that's so far beyond the
+ * practical range of NFA sizes that it doesn't seem worth additional code.)
*/
#ifndef REG_MAX_COMPILE_SPACE
#define REG_MAX_COMPILE_SPACE \
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index e9e32ab..e651c64 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -42,8 +42,6 @@ extern void InitShmemIndex(void);
extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
HASHCTL *infoP, int hash_flags);
extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
-extern Size add_size(Size s1, Size s2);
-extern Size mul_size(Size s1, Size s2);
/* ipci.c */
extern void RequestAddinShmemSpace(Size size);
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 909bc2e..d2e51ab 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -41,6 +41,7 @@
#define AllocSizeIsValid(size) ((Size) (size) <= MaxAllocSize)
+/* Do not make this any bigger; see add_size() and mul_size() */
#define MaxAllocHugeSize (SIZE_MAX / 2)
#define AllocHugeSizeIsValid(size) ((Size) (size) <= MaxAllocHugeSize)
diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h
index 5983c10..ad238d7 100644
--- a/src/include/utils/palloc.h
+++ b/src/include/utils/palloc.h
@@ -78,8 +78,22 @@ extern void *palloc(Size size);
extern void *palloc0(Size size);
extern void *palloc_extended(Size size, int flags);
extern void *repalloc(void *pointer, Size size);
+extern void *repalloc_extended(void *pointer,
+ Size size, int flags);
extern void pfree(void *pointer);
+/*
+ * Support for safe calculation of memory request sizes
+ */
+extern Size add_size(Size s1, Size s2);
+extern Size mul_size(Size s1, Size s2);
+extern void *palloc_mul(Size s1, Size s2);
+extern void *palloc0_mul(Size s1, Size s2);
+extern void *palloc_mul_extended(Size s1, Size s2, int flags);
+extern void *repalloc_mul(void *p, Size s1, Size s2);
+extern void *repalloc_mul_extended(void *p, Size s1, Size s2,
+ int flags);
+
/*
* Variants with easier notation and more type safety
*/
@@ -93,14 +107,16 @@ extern void pfree(void *pointer);
/*
* Allocate space for "count" objects of type "type"
*/
-#define palloc_array(type, count) ((type *) palloc(sizeof(type) * (count)))
-#define palloc0_array(type, count) ((type *) palloc0(sizeof(type) * (count)))
+#define palloc_array(type, count) ((type *) palloc_mul(sizeof(type), count))
+#define palloc0_array(type, count) ((type *) palloc0_mul(sizeof(type), count))
+#define palloc_array_extended(type, count, flags) ((type *) palloc_mul_extended(sizeof(type), count, flags))
/*
* Change size of allocation pointed to by "pointer" to have space for "count"
* objects of type "type"
*/
-#define repalloc_array(pointer, type, count) ((type *) repalloc(pointer, sizeof(type) * (count)))
+#define repalloc_array(pointer, type, count) ((type *) repalloc_mul(pointer, sizeof(type), count))
+#define repalloc_array_extended(pointer, type, count, flags) ((type *) repalloc_mul_extended(pointer, sizeof(type), count, flags))
/*
* The result of palloc() is always word-aligned, so we can skip testing
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 076a9d0..8cca8c0 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -24,6 +24,7 @@
#include <unistd.h>
#endif
+#include "common/int.h"
#include "libpq-fe.h"
#include "libpq-int.h"
#include "mb/pg_wchar.h"
@@ -3493,27 +3494,6 @@ PQescapeString(char *to, const char *from, size_t length)
}
-/*
- * Frontend version of the backend's add_size(), intended to be API-compatible
- * with the pg_add_*_overflow() helpers. Stores the result into *dst on success.
- * Returns true instead if the addition overflows.
- *
- * TODO: move to common/int.h
- */
-static bool
-add_size_overflow(size_t s1, size_t s2, size_t *dst)
-{
- size_t result;
-
- result = s1 + s2;
- if (result < s1 || result < s2)
- return true;
-
- *dst = result;
- return false;
-}
-
-
/*
* Escape arbitrary strings. If as_ident is true, we escape the result
* as an identifier; if false, as a literal. The result is returned in
@@ -3596,14 +3576,14 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
* Allocate output buffer. Protect against overflow, in case the caller
* has allocated a large fraction of the available size_t.
*/
- if (add_size_overflow(input_len, num_quotes, &result_size) ||
- add_size_overflow(result_size, 3, &result_size)) /* two quotes plus a NUL */
+ if (pg_add_size_overflow(input_len, num_quotes, &result_size) ||
+ pg_add_size_overflow(result_size, 3, &result_size)) /* two quotes plus a NUL */
goto overflow;
if (!as_ident && num_backslashes > 0)
{
- if (add_size_overflow(result_size, num_backslashes, &result_size) ||
- add_size_overflow(result_size, 2, &result_size)) /* for " E" prefix */
+ if (pg_add_size_overflow(result_size, num_backslashes, &result_size) ||
+ pg_add_size_overflow(result_size, 2, &result_size)) /* for " E" prefix */
goto overflow;
}
@@ -3766,9 +3746,9 @@ PQescapeByteaInternal(PGconn *conn,
if (use_hex)
{
/* We prepend "\x" and double each input character. */
- if (add_size_overflow(len, bslash_len + 1, &len) ||
- add_size_overflow(len, from_length, &len) ||
- add_size_overflow(len, from_length, &len))
+ if (pg_add_size_overflow(len, bslash_len + 1, &len) ||
+ pg_add_size_overflow(len, from_length, &len) ||
+ pg_add_size_overflow(len, from_length, &len))
goto overflow;
}
else
@@ -3778,22 +3758,22 @@ PQescapeByteaInternal(PGconn *conn,
{
if (*vp < 0x20 || *vp > 0x7e)
{
- if (add_size_overflow(len, bslash_len + 3, &len)) /* octal "\ooo" */
+ if (pg_add_size_overflow(len, bslash_len + 3, &len)) /* octal "\ooo" */
goto overflow;
}
else if (*vp == '\'')
{
- if (add_size_overflow(len, 2, &len)) /* double each quote */
+ if (pg_add_size_overflow(len, 2, &len)) /* double each quote */
goto overflow;
}
else if (*vp == '\\')
{
- if (add_size_overflow(len, bslash_len * 2, &len)) /* double each backslash */
+ if (pg_add_size_overflow(len, bslash_len * 2, &len)) /* double each backslash */
goto overflow;
}
else
{
- if (add_size_overflow(len, 1, &len))
+ if (pg_add_size_overflow(len, 1, &len))
goto overflow;
}
}
diff --git a/src/interfaces/libpq/fe-print.c b/src/interfaces/libpq/fe-print.c
index 257533e..372d7dd 100644
--- a/src/interfaces/libpq/fe-print.c
+++ b/src/interfaces/libpq/fe-print.c
@@ -33,6 +33,7 @@
#endif
#endif
+#include "common/int.h"
#include "libpq-fe.h"
#include "libpq-int.h"
@@ -469,27 +470,6 @@ do_field(const PQprintOpt *po, const PGresult *res,
}
-/*
- * Frontend version of the backend's add_size(), intended to be API-compatible
- * with the pg_add_*_overflow() helpers. Stores the result into *dst on success.
- * Returns true instead if the addition overflows.
- *
- * TODO: move to common/int.h
- */
-static bool
-add_size_overflow(size_t s1, size_t s2, size_t *dst)
-{
- size_t result;
-
- result = s1 + s2;
- if (result < s1 || result < s2)
- return true;
-
- *dst = result;
- return false;
-}
-
-
static char *
do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
const char **fieldNames, unsigned char *fieldNotNum,
@@ -510,20 +490,20 @@ do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
for (; n < nFields; n++)
{
/* Field plus separator, plus 2 extra '-' in standard format. */
- if (add_size_overflow(tot, fieldMax[n], &tot) ||
- add_size_overflow(tot, fs_len, &tot) ||
- (po->standard && add_size_overflow(tot, 2, &tot)))
+ if (pg_add_size_overflow(tot, fieldMax[n], &tot) ||
+ pg_add_size_overflow(tot, fs_len, &tot) ||
+ (po->standard && pg_add_size_overflow(tot, 2, &tot)))
goto overflow;
}
if (po->standard)
{
/* An extra separator at the front and back. */
- if (add_size_overflow(tot, fs_len, &tot) ||
- add_size_overflow(tot, fs_len, &tot) ||
- add_size_overflow(tot, 2, &tot))
+ if (pg_add_size_overflow(tot, fs_len, &tot) ||
+ pg_add_size_overflow(tot, fs_len, &tot) ||
+ pg_add_size_overflow(tot, 2, &tot))
goto overflow;
}
- if (add_size_overflow(tot, 1, &tot)) /* terminator */
+ if (pg_add_size_overflow(tot, 1, &tot)) /* terminator */
goto overflow;
border = malloc(tot);
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index a092010..12f9fde 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -27,6 +27,7 @@
#endif
#endif
+#include "common/int.h"
#include "libpq-fe.h"
#include "libpq-int.h"
#include "mb/pg_wchar.h"
@@ -2136,26 +2137,6 @@ pqBuildStartupPacket3(PGconn *conn, int *packetlen,
return startpacket;
}
-/*
- * Frontend version of the backend's add_size(), intended to be API-compatible
- * with the pg_add_*_overflow() helpers. Stores the result into *dst on success.
- * Returns true instead if the addition overflows.
- *
- * TODO: move to common/int.h
- */
-static bool
-add_size_overflow(size_t s1, size_t s2, size_t *dst)
-{
- size_t result;
-
- result = s1 + s2;
- if (result < s1 || result < s2)
- return true;
-
- *dst = result;
- return false;
-}
-
/*
* Build a startup packet given a filled-in PGconn structure.
*
@@ -2188,11 +2169,11 @@ build_startup_packet(const PGconn *conn, char *packet,
do { \
if (packet) \
strcpy(packet + packet_len, optname); \
- if (add_size_overflow(packet_len, strlen(optname) + 1, &packet_len)) \
+ if (pg_add_size_overflow(packet_len, strlen(optname) + 1, &packet_len)) \
return 0; \
if (packet) \
strcpy(packet + packet_len, optval); \
- if (add_size_overflow(packet_len, strlen(optval) + 1, &packet_len)) \
+ if (pg_add_size_overflow(packet_len, strlen(optval) + 1, &packet_len)) \
return 0; \
} while(0)
@@ -2228,7 +2209,7 @@ build_startup_packet(const PGconn *conn, char *packet,
/* Add trailing terminator */
if (packet)
packet[packet_len] = '\0';
- if (add_size_overflow(packet_len, 1, &packet_len))
+ if (pg_add_size_overflow(packet_len, 1, &packet_len))
return 0;
return packet_len;
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index 73f2d13..98c783f 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -2007,6 +2007,16 @@ NOTICE: text-search query doesn't contain lexemes: ""
foo bar
(1 row)
+-- Test for large values of StartSel, StopSel and FragmentDelimiter
+SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'),
+ 'StartSel=' || repeat('x', 32768));
+ERROR: value for "StartSel" is too long
+SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'),
+ 'StopSel=' || repeat('x', 32768));
+ERROR: value for "StopSel" is too long
+SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'),
+ 'FragmentDelimiter=' || repeat('x', 32768));
+ERROR: value for "FragmentDelimiter" is too long
--Rewrite sub system
CREATE TABLE test_tsquery (txtkeyword TEXT, txtsample TEXT);
\set ECHO none
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index a608828..ec2d4e6 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -555,6 +555,14 @@ SELECT ts_headline('english',
SELECT ts_headline('english',
'foo bar', to_tsquery('english', ''));
+-- Test for large values of StartSel, StopSel and FragmentDelimiter
+SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'),
+ 'StartSel=' || repeat('x', 32768));
+SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'),
+ 'StopSel=' || repeat('x', 32768));
+SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'),
+ 'FragmentDelimiter=' || repeat('x', 32768));
+
--Rewrite sub system
CREATE TABLE test_tsquery (txtkeyword TEXT, txtsample TEXT);