1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

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
This commit is contained in:
Tom Lane
2026-08-17 15:06:02 -04:00
parent 3d8f71a370
commit 131444cd16
2 changed files with 33 additions and 13 deletions

View File

@@ -135,12 +135,11 @@ static void
AV_to_JsonbValue(AV *in, JsonbInState *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);

View File

@@ -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);
@@ -1173,8 +1174,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,
@@ -1214,11 +1215,11 @@ array_to_datum_internal(AV *av, ArrayBuildState **astatep,
errmsg("number of array dimensions exceeds the maximum allowed (%d)",
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")));
@@ -1260,6 +1261,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
*/
@@ -1287,7 +1307,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,
@@ -2478,14 +2498,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))