summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2026-08-17 19:06:02 +0000
committerTom Lane2026-08-17 19:06:02 +0000
commit1035eab491bf86db9bbb754854ba3d02ac80f4c4 (patch)
tree2c8312f53b9ab5743e8c0a89e7a910f213a6acf4
parenta5e2588a1c89cad1b2e1f1a4fd20632b4de6f986 (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.c5
-rw-r--r--src/pl/plperl/plperl.c41
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))