1783 lines
54 KiB
Diff
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);
|