diff options
| author | Tom Lane | 2026-08-17 19:06:02 +0000 |
|---|---|---|
| committer | Tom Lane | 2026-08-17 19:06:02 +0000 |
| commit | 1035eab491bf86db9bbb754854ba3d02ac80f4c4 (patch) | |
| tree | 2c8312f53b9ab5743e8c0a89e7a910f213a6acf4 | |
| parent | a5e2588a1c89cad1b2e1f1a4fd20632b4de6f986 (diff) | |
Make plperl's handling of Perl arrays safer and more consistent.
plperl_func_handler()'s stanza for handling an arrayref result in
a SETOF function could loop forever (or at least till OOM) when
given a tied array, since av_fetch won't necessarily ever return
a null pointer in that case. Be consistent with the other places
where we traverse a perl array: call av_len() once and use len+1
as the loop limit, silently ignoring any null pointers we get back
from that range of subscripts.
But actually, Perl's preferred locution for this seems to be to
use av_count() not av_len()+1. av_count() seems better since
there's less risk of forgetting to add 1. Also, both of those
functions return Size_t (or SSize_t) not int, creating at least
a theoretical overflow hazard. While we're modernizing this,
let's use the correct variable type where we can, and include an
overflow check where we can't.
Reported-by: Claude Code (via Noah Misch)
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Discussion: https://postgr.es/m/569769.1786901901@sss.pgh.pa.us
Backpatch-through: 14
| -rw-r--r-- | contrib/jsonb_plperl/jsonb_plperl.c | 5 | ||||
| -rw-r--r-- | src/pl/plperl/plperl.c | 41 |
2 files changed, 33 insertions, 13 deletions
diff --git a/contrib/jsonb_plperl/jsonb_plperl.c b/contrib/jsonb_plperl/jsonb_plperl.c index 2f585f083a9..968ca30bd21 100644 --- a/contrib/jsonb_plperl/jsonb_plperl.c +++ b/contrib/jsonb_plperl/jsonb_plperl.c @@ -133,12 +133,11 @@ static JsonbValue * AV_to_JsonbValue(AV *in, JsonbParseState **jsonb_state) { dTHX; - SSize_t pcount = av_len(in) + 1; - SSize_t i; + Size_t pcount = av_count(in); pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL); - for (i = 0; i < pcount; i++) + for (Size_t i = 0; i < pcount; i++) { SV **value = av_fetch(in, i, FALSE); diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index 854591c4376..1cd69d544a8 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -279,6 +279,7 @@ static void array_to_datum_internal(AV *av, ArrayBuildState **astatep, int *ndims, int *dims, int cur_depth, Oid elemtypid, int32 typmod, FmgrInfo *finfo, Oid typioparam); +static int av_count_limit(AV *av); static Datum plperl_hash_to_datum(SV *src, TupleDesc td); static void plperl_init_shared_libs(pTHX); @@ -1170,8 +1171,8 @@ get_perl_array_ref(SV *sv) * is frozen). * * Caller is required to have set dims[cur_depth - 1] to the length of the - * input array, i.e., av_len(av) + 1. We make this requirement so as to - * avoid reading av_len() twice, which is hazardous for tied arrays. + * input array, i.e., av_count_limit(av). We make this requirement so as to + * avoid reading av_count() twice, which is hazardous for tied arrays. */ static void array_to_datum_internal(AV *av, ArrayBuildState **astatep, @@ -1211,11 +1212,11 @@ array_to_datum_internal(AV *av, ArrayBuildState **astatep, errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", cur_depth + 1, MAXDIM))); /* OK, add a dimension */ - dims[*ndims] = av_len(nav) + 1; + dims[*ndims] = av_count_limit(nav); (*ndims)++; } else if (cur_depth >= *ndims || - av_len(nav) + 1 != dims[cur_depth]) + av_count_limit(nav) != dims[cur_depth]) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("multidimensional arrays must have array expressions with matching dimensions"))); @@ -1258,6 +1259,25 @@ array_to_datum_internal(AV *av, ArrayBuildState **astatep, } /* + * av_count returns Size_t, so at least in theory it could overrun INT_MAX. + * As long as we have to check, let's throw error for anything above + * MaxArraySize, which will surely fail later. + */ +static int +av_count_limit(AV *av) +{ + dTHX; + Size_t cnt = av_count(av); + + if (cnt > MaxArraySize) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("array size exceeds the maximum allowed (%zu)", + MaxArraySize))); + return (int) cnt; +} + +/* * convert perl array ref to a datum */ static Datum @@ -1284,7 +1304,7 @@ plperl_array_to_datum(SV *src, Oid typid, int32 typmod) _sv_to_datum_finfo(elemtypid, &finfo, &typioparam); memset(dims, 0, sizeof(dims)); - dims[0] = av_len(nav) + 1; + dims[0] = av_count_limit(nav); array_to_datum_internal(nav, &astate, &ndims, dims, 1, @@ -2475,14 +2495,15 @@ plperl_func_handler(PG_FUNCTION_ARGS) if (sav) { dTHX; - int i = 0; - SV **svp = 0; AV *rav = (AV *) SvRV(sav); + Size_t alen = av_count(rav); - while ((svp = av_fetch(rav, i, FALSE)) != NULL) + for (Size_t i = 0; i < alen; i++) { - plperl_return_next_internal(*svp); - i++; + SV **svp = av_fetch(rav, i, FALSE); + + if (svp) + plperl_return_next_internal(*svp); } } else if (SvOK(perlret)) |
