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 +#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 #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 #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);